Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

iOS Promotional Offers Fix #220

Merged
merged 3 commits into from
Oct 11, 2020

Conversation

mgonzalezc
Copy link
Contributor

  • Fix typo in method call
  • Retrieving discounts information

@mgonzalezc mgonzalezc closed this Sep 28, 2020
@mgonzalezc mgonzalezc reopened this Sep 28, 2020
Copy link
Owner

@hyochan hyochan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like a great improvement. Could you also look at the readme if there are any changes needed?

@hyochan hyochan force-pushed the bugfix/promotional_offers branch from 8754743 to 412317a Compare October 11, 2020 04:39
@hyochan hyochan merged commit 685a703 into hyochan:master Oct 11, 2020
@hyochan hyochan added enhancement New feature or request ios ios related issue labels Oct 11, 2020
mgonzalezc pushed a commit to mgonzalezc/flutter_inapp_purchase that referenced this pull request Oct 14, 2020
* master:
  Remove meta in dep
  fix for hyochan#230 (hyochan#231)
  Release 2.3.1
  Release 2.3.0
  iOS Promotional Offers Fix (hyochan#220)
  Adding introductoryPriceNumberIOS (hyochan#214)
  IAPItem deserialization fix (hyochan#212)
  Fix a typo (eneabled -> enabled) (hyochan#216)
  Fix typo (hyochan#210)

# Conflicts:
#	lib/modules.dart
@JulianBissekkou
Copy link

JulianBissekkou commented Dec 3, 2020

I am using this API and I am getting an error saying that the user is not eligible for this offer.
Can you maybe help me with this?

  @override
  Future<void> subscribeToProduct(String productId, String offerId) => withPackage((iap) async {
        final userId = getUserId();

        final offerSignature = await _requestOfferSignature(
          productId: productId,
          offerId: offerId,
          username: userId,
        );

        final completer = Completer<void>();
        FlutterInappPurchase.purchaseUpdated.listen((event) {
          print(event);
          //TODO Finish transaction
          completer.complete();
        });

        // ignore: unawaited_futures
        iap.requestProductWithOfferIOS(productId, userId, <String, Object>{
          "identifier": offerId,
          "keyIdentifier": offerSignature.keyIdentifier,
          "nonce": offerSignature.nonce,
          "signature": offerSignature.signature,
          "timestamp": offerSignature.timestamp,
        });

        return completer.future;
      });

This is the code that I am using, the signature is created by my server.

@JulianBissekkou
Copy link

Especially @mgonzalezc since you are using this feature 👍

@mgonzalezc
Copy link
Contributor Author

Hello @JulianBissekkou!

On the one hand, the iOS promotional offers can only be purchased if the user signed up for the same subscription you are creating the signature for. For example, if you have a subscription with id julian.awesome.game, and you have created a promotional offer inside, you can only purchase the offer if you had a subscription to julian.awesome.game in the past.

You can determine the eligibility of your user in your server. For this matter, you can implement App Store Server Notifications and save if the user is eligible in your database when you detect cancelling, for example.

On the other hand, I have experienced some issues when the purchase was done a long time ago before Apple implemented promotional offers. The error thrown was the same you describe. But this has not happened to me with more recent subscriptions.

By last, is it possible that your signature is not correct?

@JulianBissekkou
Copy link

JulianBissekkou commented Dec 3, 2020

@mgonzalezc Thanks for the quick answer!

you can only purchase the offer if you had a subscription to julian.awesome.game in the past.

I am not sure if I understood this correctly. Are you saying that I have to create a subscription first and after that, I can apply the offer to it? 🤔
In my case, the user will have a trial period of 1 month or 7 days. The user has no subscriptions when I call the method.
productId is the id of the subscription and offerId is the Id of the offer which was inside of discountIOS.
Am I doing it wrong this way?

You can determine the eligibility of your user in your server. For this matter, you can implement App Store Server Notifications and save if the user is eligible in your database when you detect canceling, for example.

I didn't use the App Store Server Notification yet because it seems that I am doing something wrong. Every user should be eligible for my offer or am I missing something? 🤔

By last, is it possible that your signature is not correct?

It looks correct when I compare the format of the example signature in the docs and there is no error that tells me that the signature is wrong. I am generating the signature in my kotlin/java backend. Maybe you can share your implementation of the signature creation?

Thanks a lot for helping, really appreciate your answer! ☺️

@mgonzalezc
Copy link
Contributor Author

I am not sure if I understood this correctly. Are you saying that I have to create a subscription first and after that, I can apply the offer to it? 🤔

Yes, this is how promotional offers work. You can check this

In short, promotional offers belong to an auto-renewable subscription so if you have three different subscriptions and want to offer a 40% discount in each of them, you will need to add three promotional offers, once per subscription, inside each subscription.

In my case, the user will have a trial period of 1 month or 7 days. The user has no subscriptions when I call the method.
productId is the id of the subscription and offerId is the Id of the offer which was inside of discountIOS.
Am I doing it wrong this way?

Do you mean you have different intro prices configured for your subscription? Or do you have your own trial implementation?

If you are using intro prices, then your users have a subscription (in intro period, but a subscription either way). However, if you are using your own trial implementation, your users will not be eligible for a promotional offer. For them to be eligible, they need to buy the subscription (or trial) first from App Store.

I didn't use the App Store Server Notification yet because it seems that I am doing something wrong. Every user should be eligible for my offer or am I missing something? 🤔

They will only be eligible if they bought the subscription(or trial) before.

Maybe you can share your implementation of the signature creation?

I am using firebase functions + firebase storage for this matter:

 const productId = data.productId; //this comes from my app
 const offerId = data.offerId; //this comes from my app
 const username = data.username; //this comes from my app

 const keyId = <key_id>
 const nonce = uuidv4()
 const timestamp = new Date().getTime()
 const bucket = admin.storage().bucket();
 const filename = <path_p8_file>
 const keyFile = await bucket.file(filename).download();

 const payload = <bundle_id> + '\u2063' 
               + keyId + '\u2063' 
               + productId + '\u2063' 
               + offerId + '\u2063' 
               + username + '\u2063' 
               + nonce + '\u2063' 
               + timestamp

 
 const privateKey = keyFile[0].toString('ascii')

 const base64Signature = crypto.createSign('RSA-SHA256')
                         .update(payload)
                         .sign(privateKey, 'base64');

 const response = {
   "identifier": offerId,
   "nonce": nonce, 
   "timestamp": timestamp.toString(), 
   "keyIdentifier": keyId, 
   "signature": base64Signature,
 };

 return response;

@JulianBissekkou
Copy link

@mgonzalezc
Okay, there was a misunderstanding on my side because I tried to use the promotional offer as trial offers which doesn't work.
Quite a stupid mistake from my side. 🤦
It works now 👍

Anyways, thanks for your help and that you took the time to answer all my questions! 👍
As you saw, I am new to in-app payment, but I am quite experienced with Flutter and if you need help with a flutter related problem I can give back and help you there in the future.

Have a nice weekend!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request ios ios related issue
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants