Apple Promotional Offers

Learn how to work with Apple Promotional Offers using Qonversion SDKs

πŸ“˜

This section is about Apple Promotional Offers

Please do not confuse them with Apple Introductory Offers and App Store Promoted Purchases, which are used for new users and for promoting your products through the App Store, respectively.

Promotional offers are useful for re-engaging former subscribers or keeping current ones on board. Consider offering a limited-time discount or a free trial period for auto-renewable subscriptions on macOS, iOS, and tvOS. This can encourage lapsed users to return and help retain your existing subscriber base.

To use promotional offers in your app, you will need to complete a few steps:

  1. Create a promotional offer in App Store Connect.
  2. Generate a Private Key, download its private part, and provide it to us along with the Private Key ID.
  3. Use the new features of our SDK to obtain the promo offer and make a purchase with it.

Create a promotional offer

Add your Promotional Offer in App Store Connect. You can find the instructions in the official documentation.

Generate a Private Key

Next, you need to generate a Private Key and download its private part. See details in the documentation.

🚧

Please note that you can download this file only once, so make sure to save it securely. If you lose it, you will need to revoke the private key and create a new one.

After downloading the file, paste it into the App Store Connect Private Key field and the key identifier in the App Store Connect Key Identifier field.

Get a promotional offer and make a purchase

The promotional offers you create in App Store Connect can be accessed within our SDK by calling Qonversion.Product -> skProduct -> discounts.

You can determine whether the user is eligible for a promotional offer based on your product’s business logic. Once you decide to grant the promotional offer to a user, you need to call the following function:

// You can obtain the product and discount in any other way. This approach is used here as an example.
let mainProduct: Qonversion.Product = products["main"]
let discount: SKProductDiscount = mainProduct.skProduct?.discounts.first(where: $0.identifier == "main_promo_offer")
Qonversion.shared().getPromotionalOffer(for: mainProduct, discount: discount) { promoOffer, error in
    // show paywall with promo offer
}
// You can obtain the product and discount in any other way. This approach is used here as an example.
QONProduct *product = products[@"main"];
NSArray *discounts = [product.skProduct.discounts filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(SKProductDiscount *object, NSDictionary *bindings) {
  return [object.identifier isEqualToString:@"main_promo_offer"];
}]];
[[Qonversion sharedInstance] getPromotionalOfferForProduct:product discount:discounts.firstObject completion:^(QONPromotionalOffer * _Nullable promotionalOffer, NSError * _Nullable error) {
  // show paywall with promo offer
}];
try {
  // You can obtain the product and discount in any other way. This approach is used here as an example.
  var promo = subscriptionProduct.skProduct?.discounts?.firstWhereOrNull(
    (discount) => discount.identifier == 'my_promo_offer_id'
  );

  if (promo != null) {
    var promoOffer = await Qonversion.getSharedInstance().getPromotionalOffer(subscriptionProduct, promo);
    // handle promo offer here
  }
} on Exception catch (e) {
  // handle error here
}
try {
  // You can obtain the product and discount in any other way. This approach is used here as an example.
  const promo = subscriptionProduct.skProduct?.discounts?.find(discount =>
      discount.identifier === 'my_promo_offer_id'
  );
  
  if (promo) {
    const promoOffer = await Qonversion.getSharedInstance().getPromotionalOffer(subscriptionProduct, promo);
  }
  // handle promo offer here
} catch (e) {
    // handle error here
}
var promo = subscriptionProduct.SkProduct?.Discounts?.Find(
  discount => discount.Identifier == "my_promo_offer_id"
);

if (promo != null) {
  Qonversion.GetSharedInstance().GetPromotionalOffer(subscriptionProduct, promo, (promoOffer, error) => {
    // Handle result here
  });
}
try {
  // You can obtain the product and discount in any other way. This approach is used here as an example.
  const promo = subscriptionProduct.skProduct?.discounts?.find(discount =>
      discount.identifier === 'my_promo_offer_id'
  );
  
  if (promo) {
    const promoOffer = await Qonversion.getSharedInstance().getPromotionalOffer(subscriptionProduct, promo);
  }
  // handle promo offer here
} catch (e) {
    // handle error here
}
try {
  // You can obtain the product and discount in any other way. This approach is used here as an example.
  const promo = subscriptionProduct.skProduct?.discounts?.find(discount =>
      discount.identifier === 'my_promo_offer_id'
  );
  
  if (promo) {
    const promoOffer = await Qonversion.getSharedInstance().getPromotionalOffer(subscriptionProduct, promo);
  }
  // handle promo offer here
} catch (e) {
    // handle error here
}

After that, we will check on our server whether the user is eligible for this promotional offer. If they are, we will generate a signature for the purchase and return a Qonversion.PromotionalOffer object, which you will need to pass to the purchase function.

Determine eligibility

What availability conditions do we check for promotional offers? Those described in the official documentation. In the future, these options may be expanded.

We consider a user eligible for an offer if they have any active or expired subscription from any subscription group.

Make a purchase

To make a purchase with a promotional offer, take the object you received from the previous function, pass it into the purchase options object, and pass it to the purchaseProduct function.

let purchaseOptions = Qonversion.PurchaseOptions(promoOffer: promoOffer)
Qonversion.shared().purchaseProduct(mainProduct, options: options) { entitlements, error, cancelled in
    // handle purcahse result here
}
QONPurchaseOptions *options = [[QONPurchaseOptions alloc] initWithPromoOffer:promotionalOffer];
[[Qonversion sharedInstance] purchaseProduct:product
                                     options:purchaseOptions
                                  completion:^(NSDictionary<NSString *,QONEntitlement *> * _Nonnull entitlements,
                                               NSError * _Nullable error,
                                               BOOL cancelled) {
  // handle result here
}];
var purchaseOptions = QPurchaseOptionsBuilder()
		.setPromotionalOffer(promoOffer)
		.build();

var entitlements = await Qonversion.getSharedInstance().purchaseProduct(
  	subscriptionProduct,
  	purchaseOptions: purchaseOptions
);
const purchaseOptions = new PurchaseOptionsBuilder()
  .setPromotionalOffer(promoOffer))
  .build();
const entitlements = await Qonversion.getSharedInstance().purchaseProduct(
  subscriptionProduct,
  purchaseOptions
);
var purchaseOptions = new PurchaseOptionsBuilder()
  	.SetPromotionalOffer(promoOffer)
  	.Build();
Qonversion.GetSharedInstance().PurchaseProduct(
  subscriptionProduct,
  purchaseOptions,
  (entitlements, error, isCancelled) =>
  {
    ...
  });
const purchaseOptions = new Qonversion.PurchaseOptionsBuilder()
  .setPromotionalOffer(promoOffer))
  .build();
const entitlements = await Qonversion.getSharedInstance().purchaseProduct(
  subscriptionProduct,
  purchaseOptions
);
const purchaseOptions = new PurchaseOptionsBuilder()
  .setPromotionalOffer(promoOffer))
  .build();
const entitlements = await Qonversion.getSharedInstance().purchaseProduct(
  subscriptionProduct,
  purchaseOptions
);