voltrue2 / in-app-purchase

A Node.js module for in-App-Purchase for iOS, Android, Amazon and Windows.
http://iap.gracenode.org
Other
1.05k stars 289 forks source link

Issues with receipt validation android #201

Closed hermosillo8 closed 6 years ago

hermosillo8 commented 6 years ago

I am having issues with the receipt validation. It keeps giving me a malformed receipt response when I have the receipt as a json object as you outlined in the READ ME. Here is how the receipts look like {"data":{"packageName":"com.flowdawgy.flowdawgy","productId":"com.flowdawgy.flowdawgy.sub.monthly","purchaseTime":"1534139918221","purchaseState":"0","purchaseToken":"long series of numbers ","autoRenewing":"true"},"signature":"base 64 encoded string"}

I am using gson to make a json object in my app. I get the result above and send that to a Firebase Callable function.

the function lookes like this

var iap = require('in-app-purchase');

const receipt = data;

iap.config({
    googlePublicKeyPath: 'C:/Users/josel/functions/public_key' ,
    googleAccToken: '4/AACkohMjjHweUNsajtlzG_YbHBjq42Xnd31txewBTra9tWDStXAisyv9FiqE-_AXHF2hKbWx_OWIB6xVXCC5ZiE',
    googleRefToken: '1/Ji2mrvKDAQE8ghncwpg43OoOn6sm3ZJq5UmXaUyGcRHsXLVKBIzxqMcWIZ2zW-jR', 
    googleClientID: '412717450824-eli7dovoo73pqkic3s8gu579h7cphe7r.apps.googleusercontent.com', 
    googleClientSecret: 'KAEp7_k_xOlIB-atNzKHgQDN', 

});

iap.setup()
    .then(() => {
        console.log("receipt: " + receipt)
        iap.validate(receipt).then(onSuccess).catch(onError);

        return this

    }).catch((error) => {
        console.log("error setup: " + error);
        throw error
    });

function onSuccess(validatedData) {

    var options = {
        ignoreExpired: true // purchaseData will NOT contain exipired subscription items
    };
    var purchaseData = iap.getPurchaseData(validatedData,options);

    console.log("PurchseData : " + purchaseData);
    console.log("ValidatedData : " + validatedData);
    return purchaseData
}

function onError(error) {
    console.log("error onError: " + error);
    throw error
}

`

with this i get an error of error onError: {"error":{},"status":1,"message":"Malformed receipt"}

Just seeing if there's anything I'm missing. Seems like I have structured the receipt the way that is outlined in README yet I am still getting an error of malformed receipt. Just seeing if you have any solutions. If this helps I am using anjlab/android-in-app-billing v3 library to perform purchases. I am also using their PurchaseData object to gather my information and create the receipt. Here is the code leading up to the calling of the function.

` RecData recData = new RecData();

        recData.setPackageName(details.purchaseInfo.purchaseData.packageName);
        recData.setProductId(details.purchaseInfo.purchaseData.productId);
        recData.setPurchaseTime(details.purchaseInfo.purchaseData.purchaseTime.getTime());
        recData.setPurchaseState(details.purchaseInfo.purchaseData.purchaseState);
        recData.setPurchaseToken(details.purchaseInfo.purchaseData.purchaseToken);
        recData.setAutoRenewing(details.purchaseInfo.purchaseData.autoRenewing);

        String signatureString = details.purchaseInfo.signature;

        byte[] data = signatureString.getBytes(StandardCharsets.UTF_8);
        signature = Base64.encodeToString(data, Base64.DEFAULT);

        SendingReceipt sendingReceipt = new SendingReceipt();

        sendingReceipt.setData(recData);
        sendingReceipt.setSignature(signature);

        Gson finalGSON = new Gson();

        receiptJSON = finalGSON.toJson(sendingReceipt);

        validation(receiptJSON);`

My validation method is as follows

` void validation(String receipt){

    FirebaseFunctions.getInstance().getHttpsCallable("googleReceiptValidation").call(receipt).addOnFailureListener(SubscriptionActivity.this, new OnFailureListener() {
        @Override
        public void onFailure(@NonNull Exception e) {
            System.out.print("Error Validating with callback method.");
            errorAlert("Error Validating: " + e.getLocalizedMessage());
        }
    }).addOnSuccessListener(SubscriptionActivity.this, new OnSuccessListener<HttpsCallableResult>() {
        @Override
        public void onSuccess(HttpsCallableResult httpsCallableResult) {
            System.out.print("Validated Receipt: " + httpsCallableResult.toString());
            String stringReceipt = httpsCallableResult.toString();
            errorAlert("validatedReceipt: " + stringReceipt);
        }
    });
}`

just need to see why It is sending back the error message. Am I incorrectly making the receipt or is it due to this app being in Alpha rather than Production/ I tried putting in the test: true in iap.setup but it still gives me the same error.

Thanks for the help in advance!

voltrue2 commented 6 years ago

Hello Thank you for reporting this issue. After reading the documentation through again, I realized that there was a mistake in the doc. Receipts for Google Play has to be an object that looks like this:

var receipt = {
    data: "receipt data object",
    signature: "signature string"
};

The data part can be an object or a JSON stringified object.

I hope this clarifies the issue. Please let me know if this helps.

Cheers

hermosillo8 commented 6 years ago

Thank you that solved my problem! I am now validating and getting the correct validated data. I have one more question to ask but this one isn't pertaining to your library so I understand if there isn't a response. Afterwards this comment can be closed! But my question is I am using a onCall(data,context) to do this validation with you library. Again this function looks like this

` var iap = require('in-app-purchase');

const receipt = data;
var res;

iap.config({
    googlePublicKeyPath: 'C:/Users/josel/functions/public_key' ,
    googleAccToken: '4/AACkohMjjHweUNsajtlzG_YbHBjq42Xnd31txewBTra9tWDStXAisyv9FiqE-_AXHF2hKbWx_OWIB6xVXCC5ZiE',
    googleRefToken: '1/Ji2mrvKDAQE8ghncwpg43OoOn6sm3ZJq5UmXaUyGcRHsXLVKBIzxqMcWIZ2zW-jR', 
    googleClientID: '412717450824-eli7dovoo73pqkic3s8gu579h7cphe7r.apps.googleusercontent.com', 
    googleClientSecret: 'KAEp7_k_xOlIB-atNzKHgQDN', 

});

iap.setup()
    .then(() => {
        console.log("receipt: ", receipt)

        iap.validate(receipt).then(onSuccess).catch(onError);
        return this

    }).catch((error) => {
        console.log("error setup: " + error);
        throw error
    });

function onSuccess(validatedData) {

    var options = {
        ignoreExpired: true // purchaseData will NOT contain exipired subscription items
    };
    var purchaseData = iap.getPurchaseData(validatedData,options);

    console.log("PurchseData : ", purchaseData);
    console.log("ValidatedData : ", validatedData);
    return purchaseData
}

function onError(error) {
    console.log("error onError: " + error);
    throw error
}`

I am not getting any data in my validation function in my app. This is the validation function `void validation(JSONObject receipt){

    FirebaseFunctions.getInstance().getHttpsCallable("googleReceiptValidation").call(receipt).addOnFailureListener(SubscriptionActivity.this, new OnFailureListener() {
        @Override
        public void onFailure(@NonNull Exception e) {
            System.out.print("Error Validating with callback method.");
            errorAlert("Error Validating: " + e.getLocalizedMessage());
        }
    }).addOnSuccessListener(new OnSuccessListener<HttpsCallableResult>() {
        @Override
        public void onSuccess(HttpsCallableResult httpsCallableResult) {
            Object response = httpsCallableResult.getData();
            Gson gson = new Gson();
            String responseJSON = gson.toJson(response);
            errorAlert("ResponseJSON: " + responseJSON);
        }
    });`

As I said the response object is coming back null. I thought returning purchaseData would send that data back and be in my httpsCallableResult but it isn't. Any thoughts on how I could get that data to actually get into my httpsCallableResult?

hermosillo8 commented 6 years ago

I figured out the problem. I have to return a promise in order to return the data back to my client using firebase's HTTP onCall trigger. So my last question is do I return the iap.validate() promise or do I return the iap.setup() promise.

voltrue2 commented 6 years ago

It’s the promise from validate().

On Aug 21, 2018 at 02:53, <Jose Hermosillo (mailto:notifications@github.com)> wrote:

I figured out the problem. I have to return a promise in order to return the data back to my client using firebase's HTTP onCall trigger. So my last question is do I return the iap.validate() promise or do I return the iap.setup() promise.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub (https://github.com/voltrue2/in-app-purchase/issues/201#issuecomment-414405892), or mute the thread (https://github.com/notifications/unsubscribe-auth/ACKY4ZbOU-4HvrcLZZxK0SVtYVt0Vh-pks5uSveigaJpZM4V7Gnb).

voltrue2 commented 6 years ago

Closing