Skip to content

Commit

Permalink
product purchase lifecycle (#1885)
Browse files Browse the repository at this point in the history
* ci: replace publication for next branch

* refactor!: create a PurchaseError class (#1866)

* refactor!: remove support for default export (#1864)

* chore: clean up non existing errors (#1868)

* chore: move some logic into internal files (#1871)

* refactor: extract event-emitter to specific file (#1872)

* refactor!: make all methods take an object of params (#1873)

* integrate ios storekit 2 (#1882)

* set min ios version to 15

* consolidated buy methods

* removed checks for older versions of ios

* cleared most errors

* swiftlint

* continue migration, purchases

* return promises to class

* moved utils to ios

* clean up promises and error codes

* serialized Transactions

* removed remaining old methods, added serialization

* default to Xcode 4 spaces

* Split files

* Added more transaction methods

* removed global autofinish

* fix lint on doc

Co-authored-by: Andres Aguilar <andres.aguilar@nfl.com>

* Able to purchase products

* swift lint

* fixed lint issues, removed methods

Co-authored-by: hyochan <dooboolab@gmail.com>
Co-authored-by: Jérémy Barbet <jeremgraph@gmail.com>
Co-authored-by: Andres Aguilar <andres.aguilar@nfl.com>
  • Loading branch information
4 people authored Aug 25, 2022
1 parent 71e8a31 commit 7f9ff8e
Show file tree
Hide file tree
Showing 21 changed files with 697 additions and 1,242 deletions.
1 change: 0 additions & 1 deletion .swiftlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,5 @@ opt_in_rules:
- operator_usage_whitespace
- redundant_type_annotation

indentation: 2
vertical_whitespace_closing_braces: true
vertical_whitespace_opening_braces: true
48 changes: 48 additions & 0 deletions IapExample/ios/Configuration.storekit
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"identifier" : "5BB04EE6",
"nonRenewingSubscriptions" : [

],
"products" : [
{
"displayPrice" : "0.99",
"familyShareable" : false,
"internalID" : "FF5725DC",
"localizations" : [
{
"description" : "",
"displayName" : "",
"locale" : "en_US"
}
],
"productID" : "com.cooni.point1000",
"referenceName" : "1000 points",
"type" : "Consumable"
},
{
"displayPrice" : "2.99",
"familyShareable" : false,
"internalID" : "47DD16EA",
"localizations" : [
{
"description" : "",
"displayName" : "",
"locale" : "en_US"
}
],
"productID" : "com.cooni.point5000",
"referenceName" : "5000 points",
"type" : "NonConsumable"
}
],
"settings" : {

},
"subscriptionGroups" : [

],
"version" : {
"major" : 1,
"minor" : 2
}
}
4 changes: 4 additions & 0 deletions IapExample/ios/IapExample.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
7699B88040F8A987B510C191 /* libPods-IapExample-IapExampleTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 19F6CBCC0A4E27FBF8BF4A61 /* libPods-IapExample-IapExampleTests.a */; };
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
8553967928B5A2D700E7E3CE /* Configuration.storekit in Resources */ = {isa = PBXBuildFile; fileRef = 8553967828B5A2D700E7E3CE /* Configuration.storekit */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -42,6 +43,7 @@
5B7EB9410499542E8C5724F5 /* Pods-IapExample-IapExampleTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-IapExample-IapExampleTests.debug.xcconfig"; path = "Target Support Files/Pods-IapExample-IapExampleTests/Pods-IapExample-IapExampleTests.debug.xcconfig"; sourceTree = "<group>"; };
5DCACB8F33CDC322A6C60F78 /* libPods-IapExample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-IapExample.a"; sourceTree = BUILT_PRODUCTS_DIR; };
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = IapExample/LaunchScreen.storyboard; sourceTree = "<group>"; };
8553967828B5A2D700E7E3CE /* Configuration.storekit */ = {isa = PBXFileReference; lastKnownFileType = text; path = Configuration.storekit; sourceTree = "<group>"; };
89C6BE57DB24E9ADA2F236DE /* Pods-IapExample-IapExampleTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-IapExample-IapExampleTests.release.xcconfig"; path = "Target Support Files/Pods-IapExample-IapExampleTests/Pods-IapExample-IapExampleTests.release.xcconfig"; sourceTree = "<group>"; };
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
/* End PBXFileReference section */
Expand Down Expand Up @@ -92,6 +94,7 @@
13B07FB61A68108700A75B9A /* Info.plist */,
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */,
13B07FB71A68108700A75B9A /* main.m */,
8553967828B5A2D700E7E3CE /* Configuration.storekit */,
);
name = IapExample;
sourceTree = "<group>";
Expand Down Expand Up @@ -243,6 +246,7 @@
buildActionMask = 2147483647;
files = (
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */,
8553967928B5A2D700E7E3CE /* Configuration.storekit in Resources */,
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@
ReferencedContainer = "container:IapExample.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<StoreKitConfigurationFileReference
identifier = "../Configuration.storekit">
</StoreKitConfigurationFileReference>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
Expand Down
2 changes: 1 addition & 1 deletion IapExample/ios/Podfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
require_relative '../node_modules/react-native/scripts/react_native_pods'
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'

platform :ios, '12.4'
platform :ios, '15.0'
install! 'cocoapods', :deterministic_uuids => false

target 'IapExample' do
Expand Down
6 changes: 3 additions & 3 deletions IapExample/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ PODS:
- React-Core
- RNGestureHandler (2.5.0):
- React-Core
- RNIap (8.6.5):
- RNIap (10.0.0):
- React-Core
- RNScreens (3.15.0):
- React-Core
Expand Down Expand Up @@ -575,12 +575,12 @@ SPEC CHECKSUMS:
ReactCommon: e30ec17dfb1d4c4f3419eac254350d6abca6d5a2
RNCMaskedView: cb9670ea9239998340eaab21df13fa12a1f9de15
RNGestureHandler: bad495418bcbd3ab47017a38d93d290ebd406f50
RNIap: e9f648d00e693913f80bbe5b661a5257fb72fe93
RNIap: 6f4925d680b31ff5d252f982aad12c8b66200b31
RNScreens: 4a1af06327774490d97342c00aee0c2bafb497b7
SocketRocket: fccef3f9c5cedea1353a9ef6ada904fde10d6608
Yoga: 7ab6e3ee4ce47d7b789d1cb520163833e515f452
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a

PODFILE CHECKSUM: cfed50b11ea421296640e72457dcf62114e957f6
PODFILE CHECKSUM: 47f330ba4aa0808e88cd2a085debf346af3dc659

COCOAPODS: 1.11.3
16 changes: 2 additions & 14 deletions IapExample/src/screens/ClassSetup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
View,
} from 'react-native';
import {
clearTransactionIOS,
endConnection,
finishTransaction,
flushFailedPurchasesCachedAsPendingAndroid,
Expand Down Expand Up @@ -65,17 +64,6 @@ export class ClassSetup extends Component<{}, State> {

if (isAndroid) {
await flushFailedPurchasesCachedAsPendingAndroid();
} else {
/**
* WARNING This line should not be included in production code
* This call will call finishTransaction in all pending purchases
* on every launch, effectively consuming purchases that you might
* not have verified the receipt or given the consumer their product
*
* TL;DR you will no longer receive any updates from Apple on
* every launch for pending purchases
*/
await clearTransactionIOS();
}
} catch (error) {
if (error instanceof PurchaseError) {
Expand Down Expand Up @@ -199,7 +187,7 @@ export class ClassSetup extends Component<{}, State> {

{productList.map((product, index) => (
<Row
key={product.productId}
key={product.id}
fields={[
{
label: 'Product JSON',
Expand All @@ -211,7 +199,7 @@ export class ClassSetup extends Component<{}, State> {
>
<Button
title="Buy"
onPress={() => this.requestSubscription(product.productId)}
onPress={() => this.requestSubscription(product.id)}
/>
</Row>
))}
Expand Down
33 changes: 7 additions & 26 deletions IapExample/src/screens/Products.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ export const Products = () => {
const {
connected,
products,
promotedProductsIOS,
currentPurchase,
currentPurchaseError,
initConnectionError,
Expand Down Expand Up @@ -102,47 +101,29 @@ export const Products = () => {

{products.map((product, index) => (
<Row
key={product.productId}
key={product.id}
fields={[
{
label: 'Product Id',
value: product.productId,
value: product.id,
},
{
label: 'type',
value: product.type,
},
]}
isLast={products.length - 1 === index}
>
<Button
title="Buy"
onPress={() => handleBuyProduct(product.productId)}
onPress={() => handleBuyProduct(product.id)}
/>
</Row>
))}
</View>

<Button title="Get the products" onPress={handleGetProducts} />
</Box>

<Box>
<Heading copy="Promoted products" label="iOS only" />

{promotedProductsIOS.map((product, index) => (
<Row
key={product.productId}
fields={[
{
label: 'Product Id',
value: product.productId,
},
]}
isLast={promotedProductsIOS.length - 1 === index}
>
<Button
title="Buy a product"
onPress={() => handleBuyProduct(product.productId)}
/>
</Row>
))}
</Box>
</ScrollView>
);
};
Expand Down
11 changes: 4 additions & 7 deletions IapExample/src/screens/Subscriptions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,11 @@ export const Subscriptions = () => {

{subscriptions.map((subscription, index) => (
<Row
key={subscription.productId}
key={subscription.id}
fields={[
{
label: 'Subscription Id',
value: subscription.productId,
value: subscription.id,
},
]}
isLast={subscriptions.length - 1 === index}
Expand All @@ -68,18 +68,15 @@ export const Subscriptions = () => {
.map((ppl) => ppl.billingPeriod)
.join(',')}`}
onPress={() => {
handleBuySubscription(
subscription.productId,
offer.offerToken,
);
handleBuySubscription(subscription.id, offer.offerToken);
}}
/>
))}
{Platform.OS === 'ios' && (
<Button
title="Subscribe"
onPress={() => {
handleBuySubscription(subscription.productId);
handleBuySubscription(subscription.id);
}}
/>
)}
Expand Down
2 changes: 1 addition & 1 deletion RNIap.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Pod::Spec.new do |s|
s.license = package["license"]
s.authors = package["author"]

s.platforms = { :ios => "10.0" , :tvos => "10.0"}
s.platforms = { :ios => "15.0" , :tvos => "15.0"}
s.source = { :git => "https://github.com/dooboolab/react-native-iap.git", :tag => "#{s.version}" }

s.source_files = "ios/*.{h,m,mm,swift}"
Expand Down
22 changes: 0 additions & 22 deletions docs/docs/usage_instructions/receipt_validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,28 +57,6 @@ flow) or unstable internet connections.
For these cases we have a convenience method `getReceiptIOS()` which gets
the latest receipt for the app at any given time. The response is base64 encoded.

### iOS Purchasing process right way.

Issue regarding `valid products`

- In iOS, generally you are fetching valid products at App launching process.

If you fetch again, or fetch valid subscription, the products are added to
the array object in iOS side (Objective-C `NSMutableArray`).

This makes unexpected behavior when you fetch with a part of product lists.

For example, if you have products of `[A, B, C]`, and you call fetch function
with only `[A]`, this module returns `[A, B, C]`).

This is weird, but it works.

- But, weird result is weird, so we made a new method which remove all valid products.

If you need to clear all products, subscriptions in that array, just call
`clearProductsIOS()`, and do the fetching job again, and you will receive what
you expected.

### Example backend (Node.js)

[Here](https://github.com/mifi/in-app-subscription-example) you can find an example backend for idempotent validating of receipts on both iOS/Android and storing and serving subscription state to the client.
Loading

0 comments on commit 7f9ff8e

Please sign in to comment.