hyochan / react-native-iap

In App Purchase module for React Native!
https://react-native-iap.hyo.dev
MIT License
2.84k stars 647 forks source link

`appAccountToken` missing on the return of the `getAvailablePurchases` method #2434

Open quentinNpApnl opened 1 year ago

quentinNpApnl commented 1 year ago

Description

appAccountToken is not on the return of the getAvailablePurchases method.

Expected Behavior

appAccountToken should be on the return of the getAvailablePurchases method.

Environment:

To Reproduce Steps to reproduce the behavior:

export const getLastSubscription = async () => {
    const purchases = await RNIap.getAvailablePurchases({ automaticallyFinishRestoredTransactions: false })

    let lastSubscription = purchases?.filter(purchase => purchase?.transactionReceipt !== null)

    let uuid = Platform.select({
            ios: latestSubscription?.appAccountToken,
            android: latestSubscription?.obfuscatedAccountIdAndroid,
        })

    return {
        sku: lastSubscription?.productId,
        uuid // <------ null on iOS
    }
}

To fix the problem : I added the followings fields on the types files

// file : types/index.ts

export interface ProductPurchase {
  productId: string;
  transactionId?: string;
  transactionDate: number;
  transactionReceipt: string;
  purchaseToken?: string;
  //iOS
  quantityIOS?: number;
  originalTransactionDateIOS?: number;
  originalTransactionIdentifierIOS?: string;
  verificationResultIOS?: string;
  appAccountToken?: string; // <---- I added this
  //Android
  productIds?: string[];
  dataAndroid?: string;
  signatureAndroid?: string;
  autoRenewingAndroid?: boolean;
  purchaseStateAndroid?: PurchaseStateAndroid;
  isAcknowledgedAndroid?: boolean;
  packageNameAndroid?: string;
  developerPayloadAndroid?: string;
  obfuscatedAccountIdAndroid?: string;
  obfuscatedProfileIdAndroid?: string;
  //Amazon
  userIdAmazon?: string;
  userMarketplaceAmazon?: string;
  userJsonAmazon?: string;
  isCanceledAmazon?: boolean;
}
// file : types/appleSk2.ts

export const transactionSk2ToPurchaseMap = ({
  id,
  originalPurchaseDate,
  productID,
  purchaseDate,
  purchasedQuantity,
  originalID,
  verificationResult,
  appAccountToken // <---- I added this
}: TransactionSk2): Purchase => {
  const purchase: Purchase = {
    productId: productID,
    transactionId: String(id),
    transactionDate: purchaseDate, //??
    transactionReceipt: '', // Not available
    purchaseToken: '', //Not available
    quantityIOS: purchasedQuantity,
    originalTransactionDateIOS: originalPurchaseDate,
    originalTransactionIdentifierIOS: originalID,
    verificationResultIOS: verificationResult ?? '',
    appAccountToken: appAccountToken, // <----- I added this
  };
  return purchase;
};

Can you add this to the method ?

Prathameshlabde commented 1 year ago

https://github.com/dooboolab-community/react-native-iap/issues/2455 Can anyone please suggest on this issue Please ?

quentinNpApnl commented 1 year ago

2455 Can anyone please suggest on this issue Please ?

I added a workaround on the issue

fodjan-philip commented 1 year ago

I saw that the appAccountToken was added to the ProductPurchase TS interface, which is also used in the event sent to the purchaseUpdatedListener. However it seems like the appAccountToken is always undefined in that case?

Our setup is quite simple and the appAccountToken also appears in the App Store Server Notifications, so I don't think we did anything wrong there:

// somewhere on payment page
await requestSubscription({ ...purchase, appAccountToken: "some-uuid" });

// somewhere else
purchaseUpdatedListener((purchase) => purchase.appAccountToken); // always undefined
quentinnippert commented 1 year ago

@fodjan-philip There's probably missing properties on the data sent from the ios event. Are you using Storekit 2 ? What is the version number of react-native-iap ? Do you have the same problem on Android with the obfuscatedAccountId ?

fodjan-philip commented 1 year ago

@quentinnippert I am using StoreKit 2 and the most recent version of react-native-iap (12.10.8). obfuscatedAccountId on Android works as expected in the purchaseUpdateListener.

On iOS, I send the following data to requestSubscription:

sku: 'some-product',
appAccountToken: 'some-token'

But I only receive the following data in the purchaseUpdateListener:


originalTransactionDateIOS: 12345,
originalTransactionIdentifierIOS: '12345',
productId: 'some-product',
transactionDate: 12345,
transactionId: '54321'
transactionReceipt: '...'
quentinnippert commented 1 year ago

@fodjan-philip I'll try to take a look at this as soon as I have time for it !

fodjan-philip commented 1 year ago

@quentinnippert I think there was a misunderstanding on my side. Somehow I thought StoreKit 2 is used by default, but if I understand it correctly, it has to be activated with a setup() call? I only found that part in the migration guide for 11.0.0 by accident (never looked at the migration guides before because I started with v12 right away).

So with StoreKit 1, is it expected that I can send an appAccountToken but never get it back in the purchaseUpdateListener?

quentinnippert commented 1 year ago

@fodjan-philip Sorry for the delay. I'm not sure if you'll get you'll get the appAccountToken using StoreKit 1. It's retrieved on my project using StoreKit 2 and the last version of react-native-iap. Also make sure theappAccountToken is on a uuid format. Then, If you're handling AppStore Server Notification, you need to make sure that you received the appAccountToken on your backend.

caoyongfeng0214 commented 11 months ago

@quentinnippert I think there was a misunderstanding on my side. Somehow I thought StoreKit 2 is used by default, but if I understand it correctly, it has to be activated with a setup() call? I only found that part in the migration guide for 11.0.0 by accident (never looked at the migration guides before because I started with v12 right away).

So with StoreKit 1, is it expected that I can send an appAccountToken but never get it back in the purchaseUpdateListener?

i am using StoreKit 2. appAccountToken doesn't seem to be passed correctly because I can't always buy correctly when I use withOffer, it seems to be a signature verification error caused by the absence of appAccountToken.

quentinnippert commented 11 months ago

@caoyongfeng0214 Can you send me your code ? Are you sending a string in UUID format ?

caoyongfeng0214 commented 11 months ago

@caoyongfeng0214 Can you send me your code ? Are you sending a string in UUID format ?

Yes, it is a string.

const uuidv4 = require('uuid').v4;

const appAccountToken = uuidv4();
quentinnippert commented 11 months ago

@caoyongfeng0214 Can you send me your code ? Are you sending a string in UUID format ?

Yes, it is a string.

const uuidv4 = require('uuid').v4;

const appAccountToken = uuidv4();

I need to see how you make your purchase with react-native-iap

caoyongfeng0214 commented 11 months ago

@caoyongfeng0214 Can you send me your code ? Are you sending a string in UUID format ?

Yes, it is a string.

const uuidv4 = require('uuid').v4;

const appAccountToken = uuidv4();

I need to see how you make your purchase with react-native-iap

This is my code:

const uuidv4 = require('uuid').v4;

const appAccountToken = uuidv4();

const signResult = await http.post('https://domain/signOffer', { userName: appAccountToken });
/**
signResult:
    {
        userName: 'appAccountToken',
        withOffer: {
            identifier:'vip_free_1_month',
            keyIdentifier:'KEY_ID',
            nonce:'a-uuid',
            signature:'xxbcdxxbdijdggd......',
            timestamp: 12345678
        }
    }
**/

requestSubscription({
    sku: 'vip_month_1',
    appAccountToken: signResult.userName,
    withOffer: signResult.withOffer
});
quentinnippert commented 11 months ago

> > > > @caoyongfeng0214 Can you send me your code ? Are you sending a string in UUID format ?

Yes, it is a string.

const uuidv4 = require('uuid').v4;

const appAccountToken = uuidv4();

I need to see how you make your purchase with react-native-iap

This is my code:

const uuidv4 = require('uuid').v4;

const appAccountToken = uuidv4();

const signResult = await http.post('https://domain/signOffer', { userName: appAccountToken });
/**
signResult:
    {
        userName: 'appAccountToken',
        withOffer: {
            identifier:'vip_free_1_month',
            keyIdentifier:'KEY_ID',
            nonce:'a-uuid',
            signature:'xxbcdxxbdijdggd......',
            timestamp: 12345678
        }
    }
**/

requestSubscription({
    sku: 'vip_month_1',
    appAccountToken: signResult.userName,
    withOffer: signResult.withOffer
});

Are you using setup({storekitMode: 'STOREKIT2_MODE'}) on app launch ? (maybe in your App.js ?)

Also, make sure to setup your app store server notification on version 2 on appstore connect > select your app > app information > scroll to Appstore Server Notification > edit your endpoint. Capture d’écran 2023-11-28 à 22 02 36

caoyongfeng0214 commented 11 months ago

@caoyongfeng0214 Can you send me your code ? Are you sending a string in UUID format ?

Yes, it is a string.

const uuidv4 = require('uuid').v4;

const appAccountToken = uuidv4();

I need to see how you make your purchase with react-native-iap

This is my code:

const uuidv4 = require('uuid').v4;

const appAccountToken = uuidv4();

const signResult = await http.post('https://domain/signOffer', { userName: appAccountToken });
/**
signResult:
    {
        userName: 'appAccountToken',
        withOffer: {
            identifier:'vip_free_1_month',
            keyIdentifier:'KEY_ID',
            nonce:'a-uuid',
            signature:'xxbcdxxbdijdggd......',
            timestamp: 12345678
        }
    }
**/

requestSubscription({
    sku: 'vip_month_1',
    appAccountToken: signResult.userName,
    withOffer: signResult.withOffer
});

Are you using setup({storekitMode: 'STOREKIT2_MODE'}) on app launch ? (maybe in your App.js ?)

Also, make sure to setup your app store server notification on version 2 on appstore connect > select your app > app information > scroll to Appstore Server Notification > edit your endpoint. Capture d’écran 2023-11-28 à 22 02 36

Yes, appAccountToken is a uuid. setup({storekitMode: 'STOREKIT2_MODE'}) has been added to the App.js.

If not bring withOffer, the purchase can be completed.

If bring withOffer, the message Unable to Purchase will pop up. There is a good chance that the signature verification failed.