RevenueCat / purchases-capacitor

Capacitor in-app purchases and subscriptions made easy.
MIT License
125 stars 14 forks source link

Migration guide: @awesome-cordova-plugins to @revenuecat/capacitor-purchases [docs] #108

Open mepc36 opened 9 months ago

mepc36 commented 9 months ago

Hi, how do I solve an error message that says "Missing identifier parameter in {}"?

Here is how I'm initializing purchases in a hook:

export function usePurchases() {
  const [isPurchasesInitialized, setIsInitialized] = useState(false)

  useEffect(() => {
    (async () => {
      const { Purchases, LOG_LEVEL } = (await import('@revenuecat/purchases-capacitor'))

      if (!isPurchasesInitialized) {
        const userUuid = await getUserUuid()
        Purchases.setLogLevel({ level: LOG_LEVEL.DEBUG });
        const apiKey = Capacitor.getPlatform() === 'android' ?
          process.env.REVENUECAT_API_KEY_ANDROID :
          process.env.REVENUECAT_API_KEY_IOS

        Purchases.configure({
          apiKey,
          appUserID: userUuid
        });
        myPurchases = Purchases

        setIsInitialized(true)
      }
    })()
  }, [isPurchasesInitialized]);

  return { isPurchasesInitialized, Purchases: myPurchases };
}

This is how I get and set the purchases' offerings and customer Info:

          const offerings = await Purchases.getOfferings();
          setOfferings(offerings)
          const customerInfo = process.env.APP_ENVIRONMENT === 'development' && SUBSCRIBE_DEV_USERS ?
            DEV_CUSTOMER_INFO :
            await Purchases.getCustomerInfo()

Here is the code that runs when the "Purchase Package" button is clicked:

console.log(offeringPackage)
await Purchases.purchasePackage(offeringPackage);

Here is what the offeringPackage log in the above code prints:

{
    "identifier": "$rc_monthly",
    "packageType": "MONTHLY",
    "product": {
        "identifier": "xxxxxx",
        "description": "Gives access to pro features of the RapBot app.",
        "title": "Pro (RapWriter)",
        "price": 0.99,
        "priceString": "$0.99",
        "currencyCode": "USD",
        "introPrice": null,
        "discounts": null,
        "productCategory": "SUBSCRIPTION",
        "productType": "AUTO_RENEWABLE_SUBSCRIPTION",
        "subscriptionPeriod": "P1M",
        "defaultOption": {
            "id": "xxxxx",
            "storeProductId": "xxxxx:xxxxx",
            "productId": "xxxxx",
            "pricingPhases": [
                {
                    "billingPeriod": {
                        "unit": "MONTH",
                        "value": 1,
                        "iso8601": "P1M"
                    },
                    "recurrenceMode": 1,
                    "billingCycleCount": 0,
                    "price": {
                        "formatted": "$0.99",
                        "amountMicros": 990000,
                        "currencyCode": "USD"
                    },
                    "offerPaymentMode": null
                }
            ],
            "tags": [],
            "isBasePlan": true,
            "billingPeriod": {
                "unit": "MONTH",
                "value": 1,
                "iso8601": "P1M"
            },
            "isPrepaid": false,
            "fullPricePhase": {
                "billingPeriod": {
                    "unit": "MONTH",
                    "value": 1,
                    "iso8601": "P1M"
                },
                "recurrenceMode": 1,
                "billingCycleCount": 0,
                "price": {
                    "formatted": "$0.99",
                    "amountMicros": 990000,
                    "currencyCode": "USD"
                },
                "offerPaymentMode": null
            },
            "freePhase": null,
            "introPhase": null,
            "presentedOfferingIdentifier": "xxxxxx"
        },
        "subscriptionOptions": [
            {
                "id": "xxxxx",
                "storeProductId": "xxxx:xxxx",
                "productId": "xxxx",
                "pricingPhases": [
                    {
                        "billingPeriod": {
                            "unit": "MONTH",
                            "value": 1,
                            "iso8601": "P1M"
                        },
                        "recurrenceMode": 1,
                        "billingCycleCount": 0,
                        "price": {
                            "formatted": "$0.99",
                            "amountMicros": 990000,
                            "currencyCode": "USD"
                        },
                        "offerPaymentMode": null
                    }
                ],
                "tags": [],
                "isBasePlan": true,
                "billingPeriod": {
                    "unit": "MONTH",
                    "value": 1,
                    "iso8601": "P1M"
                },
                "isPrepaid": false,
                "fullPricePhase": {
                    "billingPeriod": {
                        "unit": "MONTH",
                        "value": 1,
                        "iso8601": "P1M"
                    },
                    "recurrenceMode": 1,
                    "billingCycleCount": 0,
                    "price": {
                        "formatted": "$0.99",
                        "amountMicros": 990000,
                        "currencyCode": "USD"
                    },
                    "offerPaymentMode": null
                },
                "freePhase": null,
                "introPhase": null,
                "presentedOfferingIdentifier": "xxxx"
            }
        ],
        "presentedOfferingIdentifier": "xxxx"
    },
    "offeringIdentifier": "xxxx"
}

What am I doing wrong?

Thanks for the plugin!

mepc36 commented 9 months ago

Looks like the package you're purchasing needs to be passed at a property inside an options object like so:

await Purchases.purchasePackage({ aPackage: offeringPackage });
mepc36 commented 9 months ago

As one more note:

this code/thread will be particularly helpful for anyone migrating from @awesome-cordova-plugins/purchases to this package. This new package is mainly a drop-in replacement, with the following changes:

  1. Don't call Purchases.configureWith, but instead call Purchases.configure
  2. This aforementioned .purchasePackage problem detailed above.
  3. .restorePurchases() does work the same
  4. Don't call Purchases.setDebugLogs, but Purchases.setLogLevel({ level: LOG_LEVEL.DEBUG }) instead.
  5. customerInfo is now nested inside a larger returned object, so instead of this...
          const customerInfo = await Purchases.getCustomerInfo()

...call this:

          const { customerInfo } = await Purchases.getCustomerInfo()

If I run into any other problems, I will report back. Thanks!

mepc36 commented 9 months ago

Leaving open for visibility, maintainers, feel free to close if you don't think a meaningful subset of your users will want this!

Martijn0405 commented 9 months ago

@mepc36 Hi! I had a question about the migration.

I am using this code on a NextJS project: import { Purchases } from "@revenuecat/purchases-capacitor"

await Purchases.configure({ apiKey, appUserID: uid })

But getting this error: Error: "CapacitorPurchases" plugin is not implemented on android

Do you maybe have any idea why it is not using Purchases instead of CapacitorPurchases?

When using: import { CapacitorPurchases } from '@capgo/capacitor-purchases';

CapacitorPurchases.setDebugLogsEnabled({ enabled: true }).then()

The app crashes instrantly, what would be a correct and functioning code solution at this moment?

aboedo commented 8 months ago

@mepc36 thanks for sharing! Sorry for the delayed response, not sure how we missed this. @codykerns @tonidero we should have a section of the docs dedicated to this specific migration, I think @mepc36 is right and it'll be a common use case.

@Martijn0405 I'd recommend performing a clean install:

tonidero commented 8 months ago

Oh sorry we missed this! We have this doc with the changes in the API: https://github.com/RevenueCat/purchases-capacitor/blob/main/migrations/v6-MIGRATION.md, but we can add a link to that in the docs. Also, I think we might be able to explain a bit more about the changes in the doc

tonidero commented 8 months ago

One question @mepc36, about this point:

customerInfo is now nested inside a larger returned object, so instead of this...

     const customerInfo = await Purchases.getCustomerInfo()

...call this:

     const { customerInfo } = await Purchases.getCustomerInfo()

If I run into any other problems, I will report back. Thanks!

This seems like it didn't change from version 5.x of the plugin. Did you update from an older version of the plugin?

Edit: Sorry, please ignore the above, you did mention you're coming from @awesome-cordova-plugins. As Andy said, we are planning to create a guide to migrate from that to this plugin. Sorry for the noise!