Android In-app purchase: how to consume? - android

I've implemented Google In-App Billing V3 in my app and i did my first test purchase. Now, as seen that i want it consumable, but if i click the "Purchase" button again i receive an error, i'm wondering how and where to insert "consumePurchase". I've been all day long on my computer searching on every thread, but i'm making confusion with old versions of the same. From what i saw, i need to call consumePurchase after the successfully purchased item AND when the activity is created, but i can't figure out how to do it.
Is this the one and only line of code?
int response = mService.consumePurchase(3, getPackageName(), token);
If so, what is "token"?
P.s. the consumable items are: 50, 150 and 300 coins that the user can buy to take a little advantage in the game.
Aaah, so confusing for me :/

As stated in the official documentetion: https://developer.android.com/google/play/billing/billing_reference.html
The response intent to the purchase includes several fields, one of them being:
INAPP_PURCHASE_DATA A String in JSON format that contains details
about the purchase order. See table 4 for a description of the JSON
fields.
Inside that JSON, you have several fields, also explained in that page, the one you are looking for is:
purchaseToken A token that uniquely identifies a purchase for a given
item and user pair.
All these is quite easy to follow from the official sample application, which I recommend you to download and try out, also to check the code.

Ok, i solved. Instead of using:
int response = mService.consumePurchase(3, getPackageName(), token);
follow this thread:
mService.consumePurchase(3, packageName, purchaseToken) always returns RESULT_DEVELOPER_ERROR = 5 - invalid arguments provided to the API

Related

Inserting developerPayload to getBuyIntent() method

I try to make Google Play Billing Service via com.android.billingclient:billing-dp1 library. I try starting the billing process by calling:
billingClient.launchBillingFlow(activity, params);
So the question is how to pass "developerPayload" argument to this flow? Both launchBillingFlow and it's params argument don't contain such a field.
Recently I opened an issue on the developers' page and they answered the following:
I just discovered that developerPayload doesn't always work. In several instances, for example when using a promo code, a later call to getPurchases() will not include the information that was sent. This limitation makes developerPayload useless. Google's official stance is that it should NOT be used for security purposes even though a bunch of documentation and the official Trivial Drive example say otherwise. Hope this saves you some time.
See details here
You cannot, if you look at the sources of BillingClientImpl you'll see this:
buyIntentBundle =
mService.getBuyIntent(3, mContext.getPackageName(), newSku, skuType, null);
The Last null is the developerPayload:
Bundle getBuyIntent(int apiVersion, String packageName, String sku, String type,
String developerPayload);
So, no implementation for developerPayload yet.
Is the InitiatePurchase command used in billing? Because you pass the developerPayload in the InitiatePurchase command...
string developerPayload = "the info you are passing in the developerPayload";
m_storeController.InitiatePurchase (product, developerPayload);
That's it.

Android In-App purchase blank order id

I am developed an app in which user purchases will registered on the server. And on the server side OrderId of Purchase class is a primary key.
In android documentation, I have read that order Id will be blank for test purchases. However, my server will not accept blank order id. So I tried this workaround:
public static final boolean IS_TEST_PURCHASES = true;
// Before updating to server
if (IS_TEST_PURCHASES && Util.isBlank(purchaseId))
purchaseId = "Test." + TimeUtils.getCurrentUnixTime();
This solved my problem for some time. But, in Beta mode the same problem appeared as I cannot release APK with IS_TEST_PURCHASES = true.
I checked the JSON retrned after purchase in which only OrderId is blank and rest of the feilds like signature, token are available. I was thinking if I can make a condition like:
if OrderId is empty and Signature or Token is not empty, then this
purchase is a test purchase and assign some dummy order id to it.
But I am confused whether this condition will cause any future problems.
I need to differentiate real purchase and test purchase so that I can set some dummy OrderId if and only if purchase is made through sandbox testing.
Any help is appreciated.
I don't know if this will help you, but what I did to differentiate between a test purchase and a real purchase was to use both the orderId and the purchaseToken as keys.
If it is a real purchase, there will be an order ID present that follows the pattern of something like GPA.ORDER_ID. If it's a test purchase, you are right there will be no order ID, but there will still be a purchase token that is a long string of random numbers / letters. So I know in my DB if it was a real purchase, the order ID will have GPA.ORDER_ID. If it doesn't follow that pattern, then it was a test purchase.

What will happen if leave "developer payload" as blank Google Play In-app Billing

Well I have read lots of time about "developer payload" But I am not clearly understand, what for "developer payload" used for. So I am trying to use this as blank like this:
public void onUpgradeAppButtonClicked(String SKU) {
Log.d(TAG,"Upgrade button clicked; launching purchase flow for upgrade.");
/*
* TODO: for security, generate your payload here for verification. See
* the comments on verifyDeveloperPayload() for more info. Since this is
* a SAMPLE, we just use an empty string, but on a production app you
* should carefully generate this.
*/
String payload = "";
mHelper.launchPurchaseFlow(this, SKU, RC_REQUEST,
mPurchaseFinishedListener, payload);
}
And this:
boolean verifyDeveloperPayload(Purchase p) {
String payload = p.getDeveloperPayload();
return true;
}
So I have make a image. for 3 Situations. I want to learn what will happen after condition
Yes the most possible shit occurs at scenario 2.
But how many users are on scenario 2? I think it would be not many. Most people don't share their devices.
But I'm thinking about another crack possibility if this payload string left empty. It would be easy to crack it down.
The only thing makes me mad is this thing should be on Google API side. Google's job to verify and make sure who purchased the item. Why we need our own server?
You should pass in a string token that helps your application to identify the user who made the purchase, so that you can later verify that this is a legitimate purchase by that user.
Think of this as a receipt. If a customer came in and wanted to return an item or warranty, you'd want to be darned sure that receipt wasn't printed at home. Using this token will help prevent fraud.
It's not required, but it is advised. According to the docs you can send an empty string, though I'm not sure about null. It's advisable for security reasons though. You can use the payload to verify that the purchase was made by the user that you intended, for instance. See the billing Security Best Practices

how to get user information in google wallet

We want to be able to associate app users with real transactions done.The problem is that we have the user’s email address on application back end side, and we track user purchase amounts, but when we go into the Google Wallet transactions we have no way of knowing which transaction/s belong to that user. We need a solution for this, because even if we have the user's email, we cannot search transactions by email address.
Is it possible to update the receipt numbers we are sending in the API to include the Google Wallet Receipt number instead of the time stamp ?
Please, provide us your suggestions.
I thought the answer would lie in the postback facility. That gives google's user ID and order number. However, I didn't see how to convert either of those to an email address for sending the digital good just purchased.
BTW. I rejected doing the fulfilment client side as that seemed insecure. If I'm wrong about that then why would they offer the postback facility?
... then I realised, we could do part of it client and part server side.
I guessed that something comes back from the client-side success callback.
success: function(result) {
console.log('success',result.response.orderId);
complete(result.response.orderId);
},
So, I now have the google's orderId on the client side and there I know the user's ID. So my complete() function can send the orderId and our userId to the server which can then match this with successful payment orderId from the postback (which happens first) and fulfil the order.
Yes, this is inelegant, but I believe it to be a secure solution.
Maybe slightly more elegant is to use the [sellerData] property in the submission payload to contain our user ID and order ref. We then have more items to match after the success callback has happened. I think I'll hold off delivering the digital good until all those checks have been completed.
What I do not understand is why cannot this kind of suggestion (or a better one) be found in the wallet tutorial?
Paul
My answer here refers to the previous answer provided:
Totally agree on "why cannot this kind of suggestion (or a better one) be found in the wallet tutorial?".
Your suggested solution does not seem to be very secured (to say the least). You want the client to send you their username/email/client-id in the Success callback... This means that anyone will be able to send you their ID, even if they did not make a purchase. They can add a random order-ID and hope to get a match (and then repeat the process many times in order to increase the chances).
My guess is that the username/email/client-id lies somewhere in the request object sent from Google to the postback URL (your server's doPost routine). But I have the feeling that you need to add something in the JWT generated in your Purchase function before it is passed to the google.payments.inapp.buy routine.
Looking for an answer myself...
Here is a possible solution, although I have not yet tested it myself:
Download the 'zip' file from: https://code.google.com/p/wallet-online-quickstart-java/downloads/list
Take the entire 'com' folder and add it to your project source folder (sorry, I have not been able to find a JAR for this package). Then, add the following code to your servlet:
...
import com.google.wallet.online.jwt.JwtResponseContainer;
import com.google.wallet.online.jwt.util.JwtGenerator;
import com.google.wallet.online.jwt.JwtResponse;
...
public void doPost(HttpServletRequest request, HttpServletResponse response) ...
{
try
{
String maskedWalletJwt = request.getParameter("maskedWalletJwt");
JwtResponseContainer jwtResponseContainer = JwtGenerator.jwtToJava(JwtResponseContainer.class, maskedWalletJwt, SellerSecret);
JwtResponse jwtResponse = jwtResponseContainer.getResponse();
String email = jwtResponse.getEmail();
...
}
}
One thing I'm not so sure about, is request.getParameter("maskedWalletJwt").
You might have to add this parameter when calling the google.payments.inapp.buy routine.

Android billing - it says checkBillingSupported() is deprecated. What should I use instead?

Android documentation says that this method is deprecated, but I do not see what else I can use instead.
Basically, I am trying to do something like this:
if (mBillingService.requestPurchase(issueProductIdPsych, Consts.ITEM_TYPE_INAPP , null))
{
// Check what happened? Did the person finish the purchase? did they cance?
if(mBillingService.checkBillingSupported(Consts.ITEM_TYPE_INAPP))
{
}
//BillingHelper.requestPurchase(mContext, "android.test.purchased");
// android.test.purchased or android.test.canceled or android.test.refunded
}
What is the correct way to accomplish checking what the end of the purchase request was?
I have a buy button like this:
buy.setOnClickListener(new Button.OnClickListener()
{
public void onClick(View v)
{
But I am not really clear what needs to be done next.
Thanks!
Wish it was simpler to just replace the code with what you need, but apparently google describes an alternative option. You'd have to implement the MarketBillingService interface. So it's just a little tweak in design. Fortunately, they show you how to accomplish this.
http://developer.android.com/guide/google/play/billing/billing_integrate.html
Go down to the topic where it says "Creating a Local Service". The subcategories are:
Binding to the MarketBillingService
Sending billing requests to the MarketBillingService
Verifying that in-app billing is supported (CHECK_BILLING_SUPPPORTED)
Making a purchase request (REQUEST_PURCHASE)
To paraphrase what was written, I'll just describe it here:
In the category: "Verifying that in-app billing is supported (CHECK_BILLING_SUPPPORTED)"
They request that you use the sendBillingRequest(). This allows you to send five different types of billing requests:
CHECK_BILLING_SUPPORTED—verifies that the Google Play application supports in-app billing and the version of the In-app Billing API available.
REQUEST_PURCHASE—sends a purchase request for an in-app item.
GET_PURCHASE_INFORMATION—retrieves transaction information for a purchase or refund.
CONFIRM_NOTIFICATIONS—acknowledges that you received the transaction information for a purchase or refund.
RESTORE_TRANSACTIONS—retrieves a user's transaction history for managed purchases.
Says you have to create a bundle before you perform the request.
Here is an example of how to perform an acknowledgement:
Bundle request = makeRequestBundle("CONFIRM_NOTIFICATIONS");
request.putStringArray(NOTIFY_IDS, mNotifyIds);
Bundle response = mService.sendBillingRequest(request);
Once you retrieve the response, check the contents within the Bundle, there will be three key items: RESPONSE_CODE, PURCHASE_INTENT, and REQUEST_ID. The RESPONSE_CODE key provides you with the status of the request and the REQUEST_ID key provides you with a unique request identifier for the request. The PURCHASE_INTENT key provides you with a PendingIntent, which you can use to launch the checkout UI.

Categories

Resources