Skip to content

Commit

Permalink
Merge pull request #1 from the-control-group/feature/intro_price
Browse files Browse the repository at this point in the history
Feature/intro price
  • Loading branch information
mehmetkoc182 committed Dec 4, 2018
2 parents 31117b5 + 974f286 commit 301f9a9
Show file tree
Hide file tree
Showing 11 changed files with 226 additions and 33 deletions.
18 changes: 4 additions & 14 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,19 +1,9 @@
sudo: false
env:
global:
- TRAVIS_NODE_VERSION="6"
- TRAVIS_NODE_VERSION="8"
matrix:
include:
- env: PLATFORM=ios-9.3
os: osx
osx_image: xcode8
language: node_js
node_js: "4"
- env: PLATFORM=ios-10.0
os: osx
osx_image: xcode9
language: node_js
node_js: "6"
- env: PLATFORM=ios-11.0
os: osx
osx_image: xcode10
Expand Down Expand Up @@ -60,14 +50,14 @@ before_install:
- rm -rf ~/.nvm && git clone https://github.com/creationix/nvm.git ~/.nvm && (cd ~/.nvm && git checkout `git describe --abbrev=0 --tags`) && source ~/.nvm/nvm.sh && nvm install $TRAVIS_NODE_VERSION
- node --version
- if [[ "$PLATFORM" =~ android ]]; then gradle --version; fi
- if [[ "$PLATFORM" =~ ios ]]; then npm install -g ios-deploy; fi
- if [[ "$PLATFORM" =~ ios ]]; then npm install -g ios-deploy > /dev/null; fi
- if [[ "$PLATFORM" =~ android ]];
then echo y | android update sdk -u --filter android-22,android-23,android-24,android-25,android-26,android-27;
fi
- npm install -g cordova
- npm install -g cordova > /dev/null

install:
- npm install
- npm install > /dev/null

script:
- make all
Expand Down
8 changes: 7 additions & 1 deletion doc/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -344,10 +344,16 @@ Products object have the following fields and methods.
- `product.state` - Current state the product is in (see [life-cycle](#life-cycle) below). Should be one of the defined [product states](#product-states)
- `product.title` - Localized name or short description
- `product.description` - Localized longer description
- `product.priceMicros` - Localized price, in micro-units (divide by 1000000 to get numeric price)
- `product.priceMicros` - Price in micro-units (divide by 1000000 to get numeric price)
- `product.price` - Localized price, with currency symbol
- `product.currency` - Currency code (optionaly)
- `product.countryCode` - Country code. Available only on iOS
- `product.introPrice` - Localized introductory price, with currency symbol. Available only on iOS
- `product.introPriceMicros` - Introductory price in micro-units (divide by 1000000 to get numeric price). Available only on iOS
- `product.introPriceNumberOfPeriods` - number of periods the introductory price is available. Available only on iOS
- `product.introPriceSubscriptionPeriod` - Period for the introductory price ("Day", "Week", "Month" or "Year"). Available only on iOS
- `product.introPricePaymentMode` - Payment mode for the introductory price ("PayAsYouGo", "UpFront", or "FreeTrial"). Available only on iOS
- `product.ineligibleForIntroPrice` - True when a trial or introductory price has been applied to a subscription. Only available after receipt validation. Available only on iOS
- `product.loaded` - Product has been loaded from server, however it can still be either `valid` or not
- `product.valid` - Product has been loaded and is a valid product
- `product.canPurchase` - Product is in a state where it can be purchased
Expand Down
34 changes: 34 additions & 0 deletions src/ios/InAppPurchase.m
Original file line number Diff line number Diff line change
Expand Up @@ -849,6 +849,35 @@ - (void)productsRequest:(SKProductsRequest*)request didReceiveResponse:(SKProduc
NSString *currencyCode = [numberFormatter currencyCode];
NSString *countryCode = [product.priceLocale objectForKey: NSLocaleCountryCode];
NSDecimalNumber *priceMicros = [product.price decimalNumberByMultiplyingByPowerOf10:6];

// Introductory price fields
NSDecimalNumber *introPriceMicros = nil;
NSString *introPricePaymentMode = nil;
NSNumber *introPriceNumberOfPeriods = nil;
NSString *introPriceSubscriptionPeriod = nil;
// Introductory price are supported from iOS 11.2
if (@available(iOS 11.2, *)) {
SKProductDiscount *introPrice = product.introductoryPrice;
if (introPrice != nil) {
introPriceMicros = [introPrice.price decimalNumberByMultiplyingByPowerOf10:6];
// https://developer.apple.com/documentation/storekit/skproductdiscountpaymentmode?language=objc
if (introPrice.paymentMode == SKProductDiscountPaymentModePayAsYouGo)
introPricePaymentMode = @"PayAsYouGo";
if (introPrice.paymentMode == SKProductDiscountPaymentModePayUpFront)
introPricePaymentMode = @"UpFront";
if (introPrice.paymentMode == SKProductDiscountPaymentModeFreeTrial)
introPricePaymentMode = @"FreeTrial";
introPriceNumberOfPeriods = [NSNumber numberWithUnsignedInt:introPrice.numberOfPeriods];
if (introPrice.subscriptionPeriod == SKProductPeriodUnitDay)
introPriceSubscriptionPeriod = @"Day";
if (introPrice.subscriptionPeriod == SKProductPeriodUnitMonth)
introPriceSubscriptionPeriod = @"Month";
if (introPrice.subscriptionPeriod == SKProductPeriodUnitWeek)
introPriceSubscriptionPeriod = @"Week";
if (introPrice.subscriptionPeriod == SKProductPeriodUnitYear)
introPriceSubscriptionPeriod = @"Year";
}
}

DLog(@"BatchProductsRequestDelegate.productsRequest:didReceiveResponse: - %@: %@", product.productIdentifier, product.localizedTitle);
[validProducts addObject:
Expand All @@ -860,6 +889,11 @@ - (void)productsRequest:(SKProductsRequest*)request didReceiveResponse:(SKProduc
NILABLE(priceMicros), @"priceMicros",
NILABLE(currencyCode), @"currency",
NILABLE(countryCode), @"countryCode",
NILABLE(product.localizedIntroPrice), @"introPrice",
NILABLE(introPriceMicros), @"introPriceMicros",
NILABLE(introPriceNumberOfPeriods), @"introPriceNumberOfPeriods",
NILABLE(introPriceSubscriptionPeriod), @"introPriceSubscriptionPeriod",
NILABLE(introPricePaymentMode), @"introPricePaymentMode",
nil]];
[self.plugin.products setObject:product forKey:[NSString stringWithFormat:@"%@", product.productIdentifier]];
}
Expand Down
1 change: 1 addition & 0 deletions src/ios/SKProduct+LocalizedPrice.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,6 @@
@interface SKProduct (LocalizedPrice)

@property (nonatomic, readonly) NSString *localizedPrice;
@property (nonatomic, readonly) NSString *localizedIntroPrice;

@end
20 changes: 20 additions & 0 deletions src/ios/SKProduct+LocalizedPrice.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,24 @@ - (NSString *)localizedPrice
return formattedString;
}

- (NSString *)localizedIntroPrice
{
// Introductory price are supported from iOS 11.2
if (@available(iOS 11.2, *)) {
SKProductDiscount *intro = self.introductoryPrice;
if (intro != nil) {
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
[numberFormatter setLocale:intro.priceLocale];
NSString *formattedString = [numberFormatter stringFromNumber:intro.price];
#if ARC_DISABLED
[numberFormatter release];
#endif
return formattedString;
}
}
return nil;
}

@end
18 changes: 12 additions & 6 deletions src/js/platforms/ios-adapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,13 +225,19 @@ function storekitLoaded(validProducts, invalidProductIds) {
p = store.products.byId[validProducts[i].id];
store.log.debug("ios -> product " + p.id + " is valid (" + p.alias + ")");
store.log.debug("ios -> owned? " + p.owned);
var v = validProducts[i];
p.set({
title: validProducts[i].title,
price: validProducts[i].price,
priceMicros: validProducts[i].priceMicros,
description: validProducts[i].description,
currency: validProducts[i].currency,
countryCode: validProducts[i].countryCode,
title: v.title,
description: v.description,
price: v.price,
priceMicros: v.priceMicros,
currency: v.currency,
countryCode: v.countryCode,
introPrice: v.introPrice,
introPriceMicros: v.introPriceMicros,
introPriceNumberOfPeriods: v.introPriceNumberOfPeriods,
introPriceSubscriptionPeriod: v.introPriceSubscriptionPeriod,
introPricePaymentMode: v.introPricePaymentMode,
state: store.VALID
});
p.trigger("loaded");
Expand Down
33 changes: 32 additions & 1 deletion src/js/product.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ store.Product = function(options) {
/// - `product.description` - Localized longer description
this.description = options.description || options.localizedDescription || null;

/// - `product.priceMicros` - Localized price, in micro-units (divide by 1000000 to get numeric price)
/// - `product.priceMicros` - Price in micro-units (divide by 1000000 to get numeric price)
this.priceMicros = options.priceMicros || null;

/// - `product.price` - Localized price, with currency symbol
Expand All @@ -55,6 +55,26 @@ store.Product = function(options) {
/// - `product.countryCode` - Country code. Available only on iOS
this.countryCode = options.countryCode || null;


/// - `product.introPrice` - Localized introductory price, with currency symbol. Available only on iOS
this.introPrice = options.introPrice || null;

/// - `product.introPriceMicros` - Introductory price in micro-units (divide by 1000000 to get numeric price). Available only on iOS
this.introPriceMicros = options.introPriceMicros || null;

/// - `product.introPriceNumberOfPeriods` - number of periods the introductory price is available. Available only on iOS
this.introPriceNumberOfPeriods = options.introPriceNumberOfPeriods || null;

/// - `product.introPriceSubscriptionPeriod` - Period for the introductory price ("Day", "Week", "Month" or "Year"). Available only on iOS
this.introPriceSubscriptionPeriod = options.introPriceSubscriptionPeriod || null;

/// - `product.introPricePaymentMode` - Payment mode for the introductory price ("PayAsYouGo", "UpFront", or "FreeTrial"). Available only on iOS
this.introPricePaymentMode = options.introPricePaymentMode || null;

/// - `product.ineligibleForIntroPrice` - True when a trial or introductory price has been applied to a subscription. Only available after receipt validation. Available only on iOS
this.ineligibleForIntroPrice = options.ineligibleForIntroPrice || null;


// - `product.localizedTitle` - Localized name or short description ready for display
// this.localizedTitle = options.localizedTitle || options.title || null;

Expand Down Expand Up @@ -167,6 +187,17 @@ store.Product.prototype.verify = function() {
store.utils.callExternal('verify.success', successCb, that, data);
store.utils.callExternal('verify.done', doneCb, that);
that.trigger("verified");

// Process the list of products that are ineligible
// for introductory prices.
if (data && data.ineligible_for_intro_price &&
data.ineligible_for_intro_price.forEach) {
data.ineligible_for_intro_price.forEach(function(pid) {
var p = store.get(pid);
if (p)
p.set('ineligibleForIntroPrice', true);
});
}
}
else {
store.log.debug("verify -> error: " + JSON.stringify(data));
Expand Down
10 changes: 8 additions & 2 deletions test/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,10 @@ function hasFile() {

# Compile for iOS
case "$OSTYPE" in darwin*)
cordova build ios || exit 1
if ! cordova build ios 2>&1 > $BUILD_DIR/build-ios.txt; then
tail -500 $BUILD_DIR/build-ios.txt
exit 1
fi

echo
echo Check iOS installation
Expand All @@ -109,7 +112,10 @@ case "$OSTYPE" in darwin*)

# Compile for Android
if [ "_$ANDROID_HOME" != "_" ]; then
cordova build android || exit 1
if ! cordova build android 2>&1 > $BUILD_DIR/build-android.txt; then
tail -500 $BUILD_DIR/build-android.txt
exit 1
fi

echo Check Android installation

Expand Down
33 changes: 32 additions & 1 deletion www/store-android.js
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,7 @@ store.Product = function(options) {
/// - `product.description` - Localized longer description
this.description = options.description || options.localizedDescription || null;

/// - `product.priceMicros` - Localized price, in micro-units (divide by 1000000 to get numeric price)
/// - `product.priceMicros` - Price in micro-units (divide by 1000000 to get numeric price)
this.priceMicros = options.priceMicros || null;

/// - `product.price` - Localized price, with currency symbol
Expand All @@ -439,6 +439,26 @@ store.Product = function(options) {
/// - `product.countryCode` - Country code. Available only on iOS
this.countryCode = options.countryCode || null;


/// - `product.introPrice` - Localized introductory price, with currency symbol. Available only on iOS
this.introPrice = options.introPrice || null;

/// - `product.introPriceMicros` - Introductory price in micro-units (divide by 1000000 to get numeric price). Available only on iOS
this.introPriceMicros = options.introPriceMicros || null;

/// - `product.introPriceNumberOfPeriods` - number of periods the introductory price is available. Available only on iOS
this.introPriceNumberOfPeriods = options.introPriceNumberOfPeriods || null;

/// - `product.introPriceSubscriptionPeriod` - Period for the introductory price ("Day", "Week", "Month" or "Year"). Available only on iOS
this.introPriceSubscriptionPeriod = options.introPriceSubscriptionPeriod || null;

/// - `product.introPricePaymentMode` - Payment mode for the introductory price ("PayAsYouGo", "UpFront", or "FreeTrial"). Available only on iOS
this.introPricePaymentMode = options.introPricePaymentMode || null;

/// - `product.ineligibleForIntroPrice` - True when a trial or introductory price has been applied to a subscription. Only available after receipt validation. Available only on iOS
this.ineligibleForIntroPrice = options.ineligibleForIntroPrice || null;


// - `product.localizedTitle` - Localized name or short description ready for display
// this.localizedTitle = options.localizedTitle || options.title || null;

Expand Down Expand Up @@ -551,6 +571,17 @@ store.Product.prototype.verify = function() {
store.utils.callExternal('verify.success', successCb, that, data);
store.utils.callExternal('verify.done', doneCb, that);
that.trigger("verified");

// Process the list of products that are ineligible
// for introductory prices.
if (data && data.ineligible_for_intro_price &&
data.ineligible_for_intro_price.forEach) {
data.ineligible_for_intro_price.forEach(function(pid) {
var p = store.get(pid);
if (p)
p.set('ineligibleForIntroPrice', true);
});
}
}
else {
store.log.debug("verify -> error: " + JSON.stringify(data));
Expand Down
51 changes: 44 additions & 7 deletions www/store-ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ store.Product = function(options) {
/// - `product.description` - Localized longer description
this.description = options.description || options.localizedDescription || null;

/// - `product.priceMicros` - Localized price, in micro-units (divide by 1000000 to get numeric price)
/// - `product.priceMicros` - Price in micro-units (divide by 1000000 to get numeric price)
this.priceMicros = options.priceMicros || null;

/// - `product.price` - Localized price, with currency symbol
Expand All @@ -460,6 +460,26 @@ store.Product = function(options) {
/// - `product.countryCode` - Country code. Available only on iOS
this.countryCode = options.countryCode || null;


/// - `product.introPrice` - Localized introductory price, with currency symbol. Available only on iOS
this.introPrice = options.introPrice || null;

/// - `product.introPriceMicros` - Introductory price in micro-units (divide by 1000000 to get numeric price). Available only on iOS
this.introPriceMicros = options.introPriceMicros || null;

/// - `product.introPriceNumberOfPeriods` - number of periods the introductory price is available. Available only on iOS
this.introPriceNumberOfPeriods = options.introPriceNumberOfPeriods || null;

/// - `product.introPriceSubscriptionPeriod` - Period for the introductory price ("Day", "Week", "Month" or "Year"). Available only on iOS
this.introPriceSubscriptionPeriod = options.introPriceSubscriptionPeriod || null;

/// - `product.introPricePaymentMode` - Payment mode for the introductory price ("PayAsYouGo", "UpFront", or "FreeTrial"). Available only on iOS
this.introPricePaymentMode = options.introPricePaymentMode || null;

/// - `product.ineligibleForIntroPrice` - True when a trial or introductory price has been applied to a subscription. Only available after receipt validation. Available only on iOS
this.ineligibleForIntroPrice = options.ineligibleForIntroPrice || null;


// - `product.localizedTitle` - Localized name or short description ready for display
// this.localizedTitle = options.localizedTitle || options.title || null;

Expand Down Expand Up @@ -572,6 +592,17 @@ store.Product.prototype.verify = function() {
store.utils.callExternal('verify.success', successCb, that, data);
store.utils.callExternal('verify.done', doneCb, that);
that.trigger("verified");

// Process the list of products that are ineligible
// for introductory prices.
if (data && data.ineligible_for_intro_price &&
data.ineligible_for_intro_price.forEach) {
data.ineligible_for_intro_price.forEach(function(pid) {
var p = store.get(pid);
if (p)
p.set('ineligibleForIntroPrice', true);
});
}
}
else {
store.log.debug("verify -> error: " + JSON.stringify(data));
Expand Down Expand Up @@ -2829,13 +2860,19 @@ function storekitLoaded(validProducts, invalidProductIds) {
p = store.products.byId[validProducts[i].id];
store.log.debug("ios -> product " + p.id + " is valid (" + p.alias + ")");
store.log.debug("ios -> owned? " + p.owned);
var v = validProducts[i];
p.set({
title: validProducts[i].title,
price: validProducts[i].price,
priceMicros: validProducts[i].priceMicros,
description: validProducts[i].description,
currency: validProducts[i].currency,
countryCode: validProducts[i].countryCode,
title: v.title,
description: v.description,
price: v.price,
priceMicros: v.priceMicros,
currency: v.currency,
countryCode: v.countryCode,
introPrice: v.introPrice,
introPriceMicros: v.introPriceMicros,
introPriceNumberOfPeriods: v.introPriceNumberOfPeriods,
introPriceSubscriptionPeriod: v.introPriceSubscriptionPeriod,
introPricePaymentMode: v.introPricePaymentMode,
state: store.VALID
});
p.trigger("loaded");
Expand Down
Loading

0 comments on commit 301f9a9

Please sign in to comment.