├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .gitattributes ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── config.yml ├── stale.yml └── workflows │ ├── ci-example-android.yml │ ├── ci-example-ios.yml │ ├── ci-ktlint-android.yml │ ├── ci.yml │ ├── code-review.yml │ ├── deploy-documentation.yml │ ├── publish-next.yml │ └── publish-package.yml ├── .gitignore ├── .monolinterrc ├── .npmignore ├── .prettierignore ├── .prettierrc.js ├── .release-it.js ├── .swiftlint.yml ├── .vscode └── settings.json ├── .watchmanconfig ├── .yarnrc ├── CHANGELOG.md ├── CONTRIBUTING.md ├── IapExample ├── .bundle │ └── config ├── .eslintrc.js ├── .gitignore ├── .prettierrc.js ├── .watchmanconfig ├── Gemfile ├── Gemfile.lock ├── README.md ├── __tests__ │ └── App.test.tsx ├── android │ ├── app │ │ ├── amazon.sdktester.json │ │ ├── build.gradle │ │ ├── debug.keystore │ │ ├── proguard-rules.pro │ │ └── src │ │ │ ├── debug │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── iapexample │ │ │ │ └── ReactNativeFlipper.java │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── iapexample │ │ │ │ │ ├── MainActivity.java │ │ │ │ │ └── MainApplication.java │ │ │ └── res │ │ │ │ ├── drawable │ │ │ │ └── rn_edit_text_material.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ └── values │ │ │ │ ├── strings.xml │ │ │ │ └── styles.xml │ │ │ └── release │ │ │ └── java │ │ │ └── com │ │ │ └── iapexample │ │ │ └── ReactNativeFlipper.java │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle ├── app.json ├── babel.config.js ├── index.tsx ├── ios │ ├── .xcode.env │ ├── Configuration.storekit │ ├── IapExample.xcodeproj │ │ ├── project.pbxproj │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── IapExample.xcscheme │ ├── IapExample.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── IapExample │ │ ├── AppDelegate.h │ │ ├── AppDelegate.mm │ │ ├── Images.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── Info.plist │ │ ├── LaunchScreen.storyboard │ │ ├── PrivacyInfo.xcprivacy │ │ └── main.m │ ├── IapExampleTests │ │ ├── IapExampleTests.m │ │ └── Info.plist │ ├── Podfile │ └── Podfile.lock ├── jest.config.js ├── metro.config.js ├── package.json ├── react-native.config.js ├── src │ ├── App.tsx │ ├── components │ │ ├── Box.tsx │ │ ├── Button.tsx │ │ ├── Heading.tsx │ │ ├── Row.tsx │ │ ├── State.tsx │ │ └── index.ts │ ├── navigators │ │ ├── StackNavigator.tsx │ │ └── index.ts │ ├── screens │ │ ├── AvailablePurchases.tsx │ │ ├── ClassSetup.tsx │ │ ├── Examples.tsx │ │ ├── Products.tsx │ │ ├── PurchaseHistory.tsx │ │ ├── Subscriptions.tsx │ │ └── index.ts │ └── utils │ │ ├── constants.ts │ │ ├── index.ts │ │ ├── logs.ts │ │ ├── platform.ts │ │ └── theme.ts ├── tsconfig.json └── yarn.lock ├── LICENSE ├── README.md ├── RNIap.podspec ├── android ├── build.gradle ├── gradle.properties └── src │ ├── amazon │ ├── AndroidManifest.xml │ └── java │ │ └── com │ │ └── dooboolab │ │ └── rniap │ │ ├── EventSender.kt │ │ ├── PurchasingServiceProxy.kt │ │ ├── PurchasingServiceProxyAmazonImpl.kt │ │ ├── RNIapActivityListener.kt │ │ ├── RNIapAmazonListener.kt │ │ ├── RNIapAmazonModule.kt │ │ └── RNIapPackage.kt │ ├── main │ ├── AndroidManifest.xml │ └── java │ │ └── com │ │ └── dooboolab │ │ └── rniap │ │ ├── PromiseUtils.kt │ │ └── PromiseUtlis.kt │ ├── play │ └── java │ │ └── com │ │ └── dooboolab │ │ └── rniap │ │ ├── PlayUtils.kt │ │ ├── RNIapActivityListener.kt │ │ ├── RNIapModule.kt │ │ └── RNIapPackage.kt │ ├── testAmazon │ └── java │ │ └── com │ │ └── dooboolab │ │ └── rniap │ │ └── RNIapAmazonModuleTest.kt │ └── testPlay │ └── java │ └── com │ └── dooboolab │ └── rniap │ └── RNIapModuleTest.kt ├── app.plugin.js ├── babel.config.js ├── bob.config.js ├── docs ├── .gitignore ├── README.md ├── babel.config.js ├── docs │ ├── api-reference │ │ ├── _category_.json │ │ ├── hooks.md │ │ └── methods │ │ │ ├── _category_.json │ │ │ ├── amazon │ │ │ ├── _category_.json │ │ │ └── validate-receipt-amazon.mdx │ │ │ ├── android │ │ │ ├── _category_.json │ │ │ ├── acknowledge-purchase-android.mdx │ │ │ ├── deep-link-to-subscriptions-android.mdx │ │ │ ├── flush-failed-purchases-cached-as-pending-android.mdx │ │ │ └── validate-receipt-android.mdx │ │ │ ├── ios │ │ │ ├── _category_.json │ │ │ ├── buy-promoted-product.mdx │ │ │ ├── clear-products-ios.mdx │ │ │ ├── clear-transaction-ios.mdx │ │ │ ├── get-pending-purchases-ios.mdx │ │ │ ├── get-promoted-product-ios.mdx │ │ │ ├── present-code-redemption-sheet-ios.mdx │ │ │ ├── request-purchase-with-offer.mdx │ │ │ ├── request-purchase-with-quantity-ios.mdx │ │ │ └── validate-receipt-ios.mdx │ │ │ └── listeners │ │ │ ├── _category_.json │ │ │ ├── promoted-product-listener.mdx │ │ │ ├── purchase-error-listener.mdx │ │ │ └── purchase-updated-listener.mdx │ ├── faq.mdx │ ├── get-started.mdx │ ├── guides │ │ ├── _category_.json │ │ ├── amazon-iap.mdx │ │ ├── lifecycle.mdx │ │ ├── purchases.mdx │ │ ├── receipts.mdx │ │ └── troubleshooting.mdx │ ├── migrate_to_10.0.0.mdx │ ├── migrate_to_11.0.0.mdx │ ├── migrate_to_12.0.0.mdx │ ├── old-to-remove-available-purchase.mdx │ ├── old-to-remove-product.mdx │ └── support-us.mdx ├── docusaurus.config.js ├── package.json ├── sidebars.js ├── src │ ├── css │ │ └── custom.css │ ├── pages │ │ └── index.js │ └── uis │ │ ├── AdFit.js │ │ └── AdFitTopFixed.js ├── static │ ├── .nojekyll │ └── img │ │ ├── favicon.ico │ │ ├── logo.png │ │ └── react-native-iapv3.svg └── yarn.lock ├── ios ├── IapSerializationUtils.swift ├── IapTypes.swift ├── IapUtils.swift ├── LatestPromiseKeeper.swift ├── RNIapIos-Bridging-Header.h ├── RNIapIos.m ├── RNIapIos.swift ├── RNIapIos.xcodeproj │ └── project.pbxproj ├── RNIapIosSk2.m ├── RNIapIosSk2.swift └── ThreadSafe.swift ├── jest.config.js ├── package.json ├── plugin ├── __tests__ │ ├── fixtures │ │ └── buildGradleFiles.ts │ └── withIAP-test.ts ├── build │ ├── withIAP.d.ts │ └── withIAP.js ├── jest.config.js ├── src │ └── withIAP.ts └── tsconfig.json ├── scripts └── bootstrap.js ├── src ├── __tests__ │ └── iap.test.ts ├── eventEmitter.ts ├── hooks │ ├── index.ts │ ├── useIAP.ts │ └── withIAPContext.tsx ├── iap.ts ├── index.ts ├── internal │ ├── enhancedFetch.ts │ ├── fillProductsWithAdditionalData.ts │ ├── index.ts │ └── platform.ts ├── modules │ ├── amazon.ts │ ├── android.ts │ ├── common.ts │ ├── index.ts │ ├── ios.ts │ └── iosSk2.ts ├── purchaseError.ts └── types │ ├── amazon.ts │ ├── android.ts │ ├── apple.ts │ ├── appleSk2.ts │ └── index.ts ├── test └── mocks │ └── react-native-modules.js ├── tsconfig.build.json ├── tsconfig.json ├── typedoc.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | 7 | [*.{js,json,yml}] 8 | charset = utf-8 9 | indent_style = space 10 | indent_size = 2 11 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.js 3 | *.d.ts 4 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: '@babel/eslint-parser', 4 | extends: ['@react-native-community', 'prettier'], 5 | plugins: ['@typescript-eslint', 'prettier', 'simple-import-sort', 'jest'], 6 | rules: { 7 | '@typescript-eslint/no-unused-vars': 'off', 8 | 'prettier/prettier': 'error', 9 | 'simple-import-sort/imports': [ 10 | 'error', 11 | { 12 | groups: [ 13 | ['^\\u0000'], 14 | ['^react', '^@?\\w'], 15 | ['^\\.\\.(?!/?$)', '^\\.\\./?$'], 16 | ['^\\./(?=.*/)(?!/?$)', '^\\.(?!/?$)', '^\\./?$'], 17 | ], 18 | }, 19 | ], 20 | }, 21 | }; 22 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | # specific for windows script files 3 | *.bat text eol=crlf 4 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | open_collective: react-native-iap 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🐛 Bug Report 3 | about: Report an issue to help us improve react-native-iap 4 | title: '' 5 | labels: 'bug' 6 | assignees: '' 7 | --- 8 | 9 | > Please use the [Discussion board](https://github.com/hyochan/react-native-iap/discussions) if you want to get some help. Please use issues to report bugs. 10 | 11 | **Description** 12 | 13 | 14 | 15 | **Expected Behavior** 16 | 17 | 18 | 19 | **Screenshots** 20 | 21 | 22 | 23 | **Environment:** 24 | 25 | - react-native-iap: 26 | - react-native: 27 | - Platforms (iOS, Android, emulator, simulator, device): 28 | 29 | **To Reproduce** 30 | Steps to reproduce the behavior: 31 | 32 | 1. Go to '...' 33 | 2. Press '...' 34 | 3. Error '...' is shown 35 | 36 | --- 37 | 38 | [Optional] **Additional Context** 39 | 40 | 41 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Configuration for probot-stale - https://github.com/probot/stale 2 | # Number of days of inactivity before an issue becomes stale 3 | daysUntilStale: 90 4 | # Number of days of inactivity before a stale issue is closed 5 | daysUntilClose: 30 6 | # Issues with these labels will never be considered stale 7 | exemptLabels: 8 | - Good first issue 9 | - For Discussion 10 | - Core Team 11 | - "Help Wanted :octocat:" 12 | - ":warning:Regression" 13 | - ":clock1:PR Pending" 14 | # Label to use when marking an issue as stale 15 | staleLabel: Stale 16 | # Comment to post when marking an issue as stale. Set to `false` to disable 17 | markComment: > 18 | Hey there, it looks like there has been no activity on this issue recently. Has the issue been fixed, or does it still require the community's attention? This issue may be closed if no further activity occurs. 19 | You may also label this issue as "For Discussion" or "Good first issue" and I will leave it open. 20 | Thank you for your contributions. 21 | # Comment to post when closing a stale issue. Set to `false` to disable 22 | closeComment: > 23 | Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please feel free to create a new issue with up-to-date information. 24 | only: issues 25 | -------------------------------------------------------------------------------- /.github/workflows/ci-example-android.yml: -------------------------------------------------------------------------------- 1 | name: CI / Example Android 2 | 3 | on: 4 | push: 5 | branches: [main, next] 6 | paths: 7 | - 'src/**' 8 | - 'android/**' 9 | - 'IapExample/android/**' 10 | - .github/ci-example-android.yml 11 | 12 | pull_request: 13 | types: [opened, synchronize, reopened] 14 | paths: 15 | - 'src/**' 16 | - 'android/**' 17 | - 'IapExample/android/**' 18 | - .github/ci-example-android.yml 19 | 20 | jobs: 21 | build_android_example: 22 | runs-on: ubuntu-latest 23 | 24 | steps: 25 | - name: Checkout repository 26 | uses: actions/checkout@v4 27 | 28 | - uses: actions/setup-java@v4 29 | with: 30 | distribution: 'zulu' 31 | java-version: '17.x' 32 | 33 | - name: Setup Node.js 34 | uses: actions/setup-node@v4 35 | with: 36 | cache: 'yarn' 37 | 38 | - name: Install dependencies 39 | run: yarn install --immutable 40 | 41 | - name: Install dependencies for `IapExample/` 42 | run: yarn install --immutable 43 | working-directory: IapExample 44 | 45 | - name: Setup kernel for react native, increase watchers 46 | run: echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p 47 | 48 | - name: Grant execute permission for gradlew in example app 49 | run: chmod +x ./gradlew 50 | working-directory: IapExample/android 51 | 52 | - name: Build with example app with Gradle 53 | run: ./gradlew build test 54 | working-directory: IapExample/android 55 | 56 | - name: Unit tests results 57 | uses: actions/upload-artifact@v4 58 | with: 59 | name: play-unit-tests-results 60 | path: IapExample/android/build/reports/tests/testPlayDebugUnitTest/index.html 61 | -------------------------------------------------------------------------------- /.github/workflows/ci-example-ios.yml: -------------------------------------------------------------------------------- 1 | name: CI / Example iOS 2 | 3 | on: 4 | push: 5 | branches: [main, next] 6 | paths: 7 | - 'src/**' 8 | - 'ios/**' 9 | - 'IapExample/ios/**' 10 | - .github/ci-example-ios.yml 11 | 12 | pull_request: 13 | types: [opened, synchronize, reopened] 14 | paths: 15 | - 'src/**' 16 | - 'ios/**' 17 | - 'IapExample/ios/**' 18 | - .github/ci-example-ios.yml 19 | 20 | jobs: 21 | build_ios_example: 22 | runs-on: macos-15 23 | env: 24 | NO_FLIPPER: 1 25 | IOS_SIMULATOR: 'platform=iOS Simulator,name=iPhone 16' 26 | 27 | steps: 28 | - name: Checkout repository 29 | uses: actions/checkout@v4 30 | 31 | - name: Setup Node.js 32 | uses: actions/setup-node@v4 33 | with: 34 | cache: 'yarn' 35 | 36 | - name: Install dependencies 37 | run: yarn install --immutable 38 | 39 | - name: Install dependencies for `IapExample/` 40 | run: yarn install --immutable 41 | working-directory: IapExample 42 | 43 | - name: Restore buildcache 44 | uses: mikehardy/buildcache-action@v2 45 | continue-on-error: true 46 | with: 47 | cache_key: ${{ runner.os }}-buildcache-${{ hashFiles('**/Podfile.lock') }}-${{ hashFiles('**/Podfile')}} 48 | 49 | - name: Setup Ruby (bundle) 50 | uses: ruby/setup-ruby@v1 51 | with: 52 | working-directory: IapExample 53 | bundler-cache: true 54 | ruby-version: '2.7' 55 | 56 | - name: Install SwiftLint 57 | run: brew install swiftlint 58 | 59 | - name: SwiftLint 60 | run: yarn lint:swift 61 | 62 | - name: Verify no files have changed after auto-fix 63 | run: git diff --exit-code HEAD '*.swift' 64 | 65 | - name: Restore Pods cache 66 | uses: actions/cache@v4 67 | with: 68 | path: | 69 | IapExample/ios/Pods 70 | ~/Library/Caches/CocoaPods 71 | ~/.cocoapods 72 | key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock')}}-${{ hashFiles('**/Podfile')}} 73 | restore-keys: ${{ runner.os }}-pods- 74 | 75 | - name: Install Pods 76 | run: bundle exec pod install 77 | working-directory: IapExample/ios 78 | 79 | - name: Install xcpretty 80 | run: gem install xcpretty 81 | working-directory: IapExample/ios 82 | 83 | - name: Build App 84 | uses: sersoft-gmbh/xcodebuild-action@v3 85 | with: 86 | workspace: IapExample/ios/IapExample.xcworkspace 87 | scheme: IapExample 88 | sdk: iphonesimulator 89 | destination: ${{ env.IOS_SIMULATOR }} 90 | action: build CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO 91 | -------------------------------------------------------------------------------- /.github/workflows/ci-ktlint-android.yml: -------------------------------------------------------------------------------- 1 | name: CI / Ktlint Android 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - .github/workflows/ci-ktlint-android.yml 7 | - 'android/src/**/*.kt' 8 | - '**.kts' 9 | 10 | jobs: 11 | ktlint: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | - run: | 16 | curl -sSLO https://github.com/pinterest/ktlint/releases/download/0.48.2/ktlint && chmod a+x ktlint && sudo mv ktlint /usr/local/bin/ 17 | - name: run ktlint 18 | run: | 19 | ktlint --reporter=checkstyle,output=build/ktlint-report.xml 20 | continue-on-error: true 21 | - uses: yutailang0119/action-ktlint@v3 22 | with: 23 | report-path: build/*.xml # Support glob patterns by https://www.npmjs.com/package/@actions/glob 24 | continue-on-error: false # If annotations contain error of severity, action-ktlint exit 1. -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [main, next] 6 | paths: 7 | - 'docs/**' 8 | - 'src/**' 9 | - 'scripts/**' 10 | - 'test/**' 11 | - '*.md' 12 | - '*.json' 13 | - '*.js' 14 | - '*.lock' 15 | - 'IapExample/src/**' 16 | - 'IapExample/*.json' 17 | - 'IapExample/*.js' 18 | - 'IapExample/*.lock' 19 | - 'IapExample/*.tsx' 20 | - .github/** 21 | 22 | pull_request: 23 | types: [opened, synchronize, reopened] 24 | paths: 25 | - 'docs/**' 26 | - 'src/**' 27 | - 'scripts/**' 28 | - 'test/**' 29 | - '*.md' 30 | - '*.json' 31 | - '*.js' 32 | - '*.lock' 33 | - 'IapExample/src/**' 34 | - 'IapExample/*.json' 35 | - 'IapExample/*.js' 36 | - 'IapExample/*.lock' 37 | - 'IapExample/*.tsx' 38 | - .github/** 39 | 40 | jobs: 41 | build: 42 | runs-on: ubuntu-latest 43 | 44 | steps: 45 | - name: Checkout repository 46 | uses: actions/checkout@v3 47 | 48 | - name: Setup Node.js 49 | uses: actions/setup-node@v3 50 | with: 51 | cache: 'yarn' 52 | 53 | - name: Install reviewdog 54 | uses: reviewdog/action-setup@v1 55 | 56 | - name: Install dependencies 57 | run: yarn install --immutable 58 | 59 | - name: Install node_modules for `IapExample/` 60 | run: yarn install --immutable 61 | working-directory: IapExample 62 | 63 | - name: Run TypeScript 64 | run: | 65 | yarn lint:tsc | reviewdog -name="tsc" -efm="%f(%l,%c): error TS%n: %m" -reporter="github-pr-review" -filter-mode="nofilter" -fail-on-error -tee 66 | env: 67 | REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} 68 | 69 | - name: Run lint scripts 70 | run: yarn lint:ci 71 | 72 | -------------------------------------------------------------------------------- /.github/workflows/code-review.yml: -------------------------------------------------------------------------------- 1 | name: Code Review GPT 2 | 3 | on: 4 | pull_request: 5 | branches: [main] 6 | 7 | permissions: 8 | pull-requests: write 9 | contents: read 10 | 11 | jobs: 12 | run_code_review: 13 | if: github.repository_owner == github.actor 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v3 17 | with: 18 | fetch-depth: 0 19 | 20 | - name: Code Review GPT 21 | uses: mattzcarey/code-review-gpt@v0.1.8 22 | with: 23 | OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} 24 | MODEL: 'gpt-4o' 25 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 26 | -------------------------------------------------------------------------------- /.github/workflows/deploy-documentation.yml: -------------------------------------------------------------------------------- 1 | name: Deploy / Documentation 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | push: 7 | branches: [main] 8 | paths: 9 | - 'docs/**' 10 | - 'src/**' 11 | 12 | jobs: 13 | publish-documentation: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - name: Checkout repository 18 | uses: actions/checkout@v3 19 | 20 | - name: Setup Node.js 21 | uses: actions/setup-node@v3 22 | with: 23 | cache: 'yarn' 24 | 25 | - name: Install packages and generate docs 26 | run: | 27 | yarn install 28 | pushd ./IapExample 29 | yarn install 30 | popd 31 | yarn typedoc --plugin typedoc-plugin-markdown --out docs/docs/api --entryDocument '..' ./src || true 32 | 33 | - name: Install packages and build 34 | run: | 35 | pushd ./docs 36 | yarn 37 | yarn build 38 | popd 39 | 40 | - name: Deploy 41 | uses: peaceiris/actions-gh-pages@v3 42 | with: 43 | github_token: ${{ secrets.GITHUB_TOKEN }} 44 | publish_dir: ./docs/build 45 | publish_branch: gh-pages 46 | cname: react-native-iap.hyo.dev 47 | -------------------------------------------------------------------------------- /.github/workflows/publish-next.yml: -------------------------------------------------------------------------------- 1 | name: Publish next package 2 | 3 | on: 4 | release: 5 | types: [created] 6 | 7 | jobs: 8 | deploy: 9 | if: github.event.release.prerelease 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout repository 14 | uses: actions/checkout@v3 15 | 16 | - name: Setup Node.js 17 | uses: actions/setup-node@v3 18 | with: 19 | cache: 'yarn' 20 | registry-url: 'https://registry.npmjs.org' 21 | 22 | - name: Remove example code 23 | run: rm -rf IapExample 24 | 25 | - name: Install dependencies 26 | run: yarn install --immutable 27 | 28 | - name: Build typescript 29 | run: yarn lint:ci 30 | 31 | - name: Verify no files have changed after auto-fix 32 | run: git diff -- ":(exclude)IapExample/*" --exit-code HEAD 33 | 34 | - run: npm publish --tag next 35 | env: 36 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 37 | -------------------------------------------------------------------------------- /.github/workflows/publish-package.yml: -------------------------------------------------------------------------------- 1 | name: Publish main package 2 | 3 | on: 4 | workflow_dispatch: 5 | release: 6 | types: [created] 7 | 8 | jobs: 9 | deploy: 10 | if: '!github.event.release.prerelease' 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout repository 14 | uses: actions/checkout@v3 15 | 16 | - name: Setup Node.js 17 | uses: actions/setup-node@v3 18 | with: 19 | cache: 'yarn' 20 | registry-url: 'https://registry.npmjs.org' 21 | 22 | - name: Remove example code 23 | run: rm -rf IapExample 24 | 25 | - name: Install dependencies 26 | run: yarn install --immutable 27 | 28 | - name: Run lint scripts 29 | run: yarn lint:ci 30 | 31 | - name: Verify no files have changed after auto-fix 32 | run: git diff -- ":(exclude)IapExample/*" --exit-code HEAD 33 | 34 | - run: npm publish 35 | env: 36 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | .yarn* 6 | # XDE 7 | # 8 | .expo/ 9 | 10 | # VSCode 11 | # 12 | jsconfig.json 13 | 14 | # Xcode 15 | # 16 | build/ 17 | *.pbxuser 18 | !default.pbxuser 19 | *.mode1v3 20 | !default.mode1v3 21 | *.mode2v3 22 | !default.mode2v3 23 | *.perspectivev3 24 | !default.perspectivev3 25 | xcuserdata 26 | *.xccheckout 27 | *.moved-aside 28 | DerivedData 29 | *.hmap 30 | *.ipa 31 | *.xcuserstate 32 | project.xcworkspace 33 | 34 | # Android/IJ 35 | # 36 | .classpath 37 | .cxx 38 | .gradle 39 | .idea 40 | .project 41 | .settings 42 | local.properties 43 | android.iml 44 | 45 | # Cocoapods 46 | # 47 | IapExample/ios/Pods 48 | 49 | # Ruby 50 | # 51 | IapExample/vendor/ 52 | 53 | # node.js 54 | # 55 | node_modules/ 56 | npm-debug.log 57 | yarn-debug.log 58 | yarn-error.log 59 | 60 | # BUCK 61 | # 62 | buck-out/ 63 | \.buckd/ 64 | android/app/libs 65 | android/keystores/debug.keystore 66 | 67 | # Expo 68 | # 69 | .expo/* 70 | 71 | # generated by bob 72 | # 73 | lib/ 74 | 75 | # yarn@2 76 | # 77 | .pnp.* 78 | **/.yarn/* 79 | !**/.yarn/patches 80 | !**/.yarn/plugins 81 | !**/.yarn/releases 82 | !**/.yarn/sdks 83 | !**/.yarn/versions 84 | 85 | # Docs 86 | docs/docs/api/* 87 | 88 | # Keep expo plugin build code 89 | !plugin/build -------------------------------------------------------------------------------- /.monolinterrc: -------------------------------------------------------------------------------- 1 | { 2 | "include": [ 3 | "package.json", 4 | "docs/package.json", 5 | "IapExample/package.json" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | IapExample/ 2 | .idea/ 3 | gen/ 4 | node_modules/ 5 | android/react-native-iap.iml 6 | android/android.iml 7 | tsconfig.tsbuildinfo 8 | *.ts 9 | *.tsx 10 | !*.d.ts 11 | tsconfig.json 12 | react-native-iap-*.tgz 13 | .vscode/ 14 | docs/ 15 | .github/ 16 | *.md 17 | .eslintrc.js 18 | .eslintignore 19 | .prettierrc.js 20 | .gitattributes 21 | .yarn/cache 22 | package/ 23 | plugin/src 24 | plugin/jest.config.js 25 | plugin/tsconfig.json 26 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | IapExample/vendor 2 | IapExample/ios 3 | IapExample/android 4 | lib 5 | ios 6 | android 7 | docs/.docusaurus 8 | docs/docs/api 9 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | trailingComma: 'all', 3 | arrowParens: 'always', 4 | singleQuote: true, 5 | jsxSingleQuote: false, 6 | bracketSpacing: false, 7 | }; 8 | -------------------------------------------------------------------------------- /.release-it.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | git: { 3 | commitMessage: 'chore: release ${version}', 4 | tagName: 'v${version}', 5 | }, 6 | npm: { 7 | publish: true, 8 | }, 9 | github: { 10 | release: true, 11 | }, 12 | plugins: { 13 | '@release-it/conventional-changelog': { 14 | preset: 'angular', 15 | }, 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | opt_in_rules: 2 | - vertical_whitespace_between_cases 3 | - vertical_whitespace_closing_braces 4 | - vertical_whitespace_opening_braces 5 | - vertical_parameter_alignment_on_call 6 | - operator_usage_whitespace 7 | - redundant_type_annotation 8 | 9 | vertical_whitespace_closing_braces: true 10 | vertical_whitespace_opening_braces: true 11 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "eslint.validate": [ 3 | "javascript", 4 | "javascriptreact", 5 | "typescript", 6 | "typescriptreact" 7 | ], 8 | "editor.codeActionsOnSave": { 9 | "source.fixAll": "explicit" 10 | }, 11 | "[javascript]": { 12 | "editor.defaultFormatter": "esbenp.prettier-vscode" 13 | }, 14 | "[javascriptreact]": { 15 | "editor.defaultFormatter": "esbenp.prettier-vscode" 16 | }, 17 | "[typescript]": { 18 | "editor.defaultFormatter": "esbenp.prettier-vscode" 19 | }, 20 | "[typescriptreact]": { 21 | "editor.defaultFormatter": "esbenp.prettier-vscode" 22 | }, 23 | "javascript.preferences.importModuleSpecifier": "relative", 24 | "typescript.preferences.importModuleSpecifier": "relative", 25 | "prettier.configPath": ".prettierrc.js", 26 | "cSpell.words": [ 27 | "crossplatformkorea", 28 | "hyochan" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /.yarnrc: -------------------------------------------------------------------------------- 1 | # Override Yarn command so we can automatically setup the repo on running `yarn` 2 | 3 | yarn-path "scripts/bootstrap.js" 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Changelog 2 | 3 | Check out the [release notes](https://github.com/hyochan/react-native-iap/releases). 4 | -------------------------------------------------------------------------------- /IapExample/.bundle/config: -------------------------------------------------------------------------------- 1 | BUNDLE_PATH: "vendor/bundle" 2 | BUNDLE_FORCE_RUBY_PLATFORM: 1 3 | -------------------------------------------------------------------------------- /IapExample/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: '@react-native', 4 | }; 5 | -------------------------------------------------------------------------------- /IapExample/.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | ios/.xcode.env.local 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | *.hprof 33 | .cxx/ 34 | *.keystore 35 | !debug.keystore 36 | 37 | # node.js 38 | # 39 | node_modules/ 40 | npm-debug.log 41 | yarn-error.log 42 | 43 | # fastlane 44 | # 45 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 46 | # screenshots whenever they are needed. 47 | # For more information about the recommended setup visit: 48 | # https://docs.fastlane.tools/best-practices/source-control/ 49 | 50 | **/fastlane/report.xml 51 | **/fastlane/Preview.html 52 | **/fastlane/screenshots 53 | **/fastlane/test_output 54 | 55 | # Bundle artifact 56 | *.jsbundle 57 | 58 | # Ruby / CocoaPods 59 | /ios/Pods/ 60 | /vendor/bundle/ 61 | 62 | # Temporary files created by Metro to check the health of the file watcher 63 | .metro-health-check* 64 | 65 | # testing 66 | /coverage -------------------------------------------------------------------------------- /IapExample/.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | arrowParens: 'avoid', 3 | bracketSameLine: true, 4 | bracketSpacing: false, 5 | singleQuote: true, 6 | trailingComma: 'all', 7 | }; 8 | -------------------------------------------------------------------------------- /IapExample/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /IapExample/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # You may use http://rbenv.org/ or https://rvm.io/ to install and use this version 4 | ruby ">= 2.6.10" 5 | 6 | gem 'cocoapods', '~> 1.13' 7 | gem 'activesupport', '>= 6.1.7.3', '< 7.1.0' 8 | -------------------------------------------------------------------------------- /IapExample/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | CFPropertyList (3.0.6) 5 | rexml 6 | activesupport (6.1.7.6) 7 | concurrent-ruby (~> 1.0, >= 1.0.2) 8 | i18n (>= 1.6, < 2) 9 | minitest (>= 5.1) 10 | tzinfo (~> 2.0) 11 | zeitwerk (~> 2.3) 12 | addressable (2.8.5) 13 | public_suffix (>= 2.0.2, < 6.0) 14 | algoliasearch (1.27.5) 15 | httpclient (~> 2.8, >= 2.8.3) 16 | json (>= 1.5.1) 17 | atomos (0.1.3) 18 | claide (1.1.0) 19 | cocoapods (1.14.3) 20 | addressable (~> 2.8) 21 | claide (>= 1.0.2, < 2.0) 22 | cocoapods-core (= 1.14.3) 23 | cocoapods-deintegrate (>= 1.0.3, < 2.0) 24 | cocoapods-downloader (>= 2.1, < 3.0) 25 | cocoapods-plugins (>= 1.0.0, < 2.0) 26 | cocoapods-search (>= 1.0.0, < 2.0) 27 | cocoapods-trunk (>= 1.6.0, < 2.0) 28 | cocoapods-try (>= 1.1.0, < 2.0) 29 | colored2 (~> 3.1) 30 | escape (~> 0.0.4) 31 | fourflusher (>= 2.3.0, < 3.0) 32 | gh_inspector (~> 1.0) 33 | molinillo (~> 0.8.0) 34 | nap (~> 1.0) 35 | ruby-macho (>= 2.3.0, < 3.0) 36 | xcodeproj (>= 1.23.0, < 2.0) 37 | cocoapods-core (1.14.3) 38 | activesupport (>= 5.0, < 8) 39 | addressable (~> 2.8) 40 | algoliasearch (~> 1.0) 41 | concurrent-ruby (~> 1.1) 42 | fuzzy_match (~> 2.0.4) 43 | nap (~> 1.0) 44 | netrc (~> 0.11) 45 | public_suffix (~> 4.0) 46 | typhoeus (~> 1.0) 47 | cocoapods-deintegrate (1.0.5) 48 | cocoapods-downloader (2.1) 49 | cocoapods-plugins (1.0.0) 50 | nap 51 | cocoapods-search (1.0.1) 52 | cocoapods-trunk (1.6.0) 53 | nap (>= 0.8, < 2.0) 54 | netrc (~> 0.11) 55 | cocoapods-try (1.2.0) 56 | colored2 (3.1.2) 57 | concurrent-ruby (1.2.2) 58 | escape (0.0.4) 59 | ethon (0.16.0) 60 | ffi (>= 1.15.0) 61 | ffi (1.16.3) 62 | fourflusher (2.3.1) 63 | fuzzy_match (2.0.4) 64 | gh_inspector (1.1.3) 65 | httpclient (2.8.3) 66 | i18n (1.14.1) 67 | concurrent-ruby (~> 1.0) 68 | json (2.7.0) 69 | minitest (5.20.0) 70 | molinillo (0.8.0) 71 | nanaimo (0.3.0) 72 | nap (1.1.0) 73 | netrc (0.11.0) 74 | public_suffix (4.0.7) 75 | rexml (3.2.8) 76 | strscan (>= 3.0.9) 77 | ruby-macho (2.5.1) 78 | strscan (3.1.0) 79 | typhoeus (1.4.1) 80 | ethon (>= 0.9.0) 81 | tzinfo (2.0.6) 82 | concurrent-ruby (~> 1.0) 83 | xcodeproj (1.23.0) 84 | CFPropertyList (>= 2.3.3, < 4.0) 85 | atomos (~> 0.1.3) 86 | claide (>= 1.0.2, < 2.0) 87 | colored2 (~> 3.1) 88 | nanaimo (~> 0.3.0) 89 | rexml (~> 3.2.4) 90 | zeitwerk (2.6.12) 91 | 92 | PLATFORMS 93 | ruby 94 | 95 | DEPENDENCIES 96 | activesupport (>= 6.1.7.3, < 7.1.0) 97 | cocoapods (~> 1.13) 98 | 99 | RUBY VERSION 100 | ruby 2.6.10p210 101 | 102 | BUNDLED WITH 103 | 1.17.2 104 | -------------------------------------------------------------------------------- /IapExample/README.md: -------------------------------------------------------------------------------- 1 | This is a new [**React Native**](https://reactnative.dev) project, bootstrapped using [`@react-native-community/cli`](https://github.com/react-native-community/cli). 2 | 3 | # Getting Started with IapExample 4 | 5 | > **Note**: Make sure you have completed the [React Native - Environment Setup](https://reactnative.dev/docs/environment-setup) instructions till "Creating a new application" step, before proceeding. 6 | 7 | ## Step 1: Install dependencies 8 | 9 | Install _node_modules_ : 10 | 11 | ```bash 12 | # using npm 13 | npm install 14 | 15 | # OR using Yarn 16 | yarn install 17 | ``` 18 | 19 | ### For iOS 20 | 21 | Install pods: 22 | 23 | ```bash 24 | cd ios 25 | pod install 26 | ``` 27 | 28 | ## Step 2: Start the Metro Server 29 | 30 | First, you will need to start **Metro**, the JavaScript _bundler_ that ships _with_ React Native. 31 | 32 | To start Metro, run the following command from the _root_ of your React Native project: 33 | 34 | ```bash 35 | # using npm 36 | npm start 37 | 38 | # OR using Yarn 39 | yarn start 40 | ``` 41 | 42 | ## Step 3: Start your Application 43 | 44 | Let Metro Bundler run in its _own_ terminal. Open a _new_ terminal from the _root_ of your React Native project. Run the following command to start your _Android_ or _iOS_ app: 45 | 46 | ### For Android 47 | 48 | ```bash 49 | # using npm 50 | npm run android:play 51 | 52 | # OR using Yarn 53 | yarn android:play 54 | ``` 55 | 56 | ### For iOS 57 | 58 | ```bash 59 | # using npm 60 | npm run ios 61 | 62 | # OR using Yarn 63 | yarn ios 64 | ``` 65 | 66 | If everything is set up _correctly_, you should see your new app running in your _Android Emulator_ or _iOS Simulator_ shortly provided you have set up your emulator/simulator correctly. 67 | 68 | This is one way to run your app — you can also run it directly from within Android Studio and Xcode respectively. 69 | 70 | ## Step 4: Modifying your App 71 | 72 | Now that you have successfully run the app, let's modify it. 73 | 74 | 1. Open `App.tsx` in your text editor of choice and edit some lines. 75 | 2. For **Android**: Press the R key twice or select **"Reload"** from the **Developer Menu** (Ctrl + M (on Window and Linux) or Cmd ⌘ + M (on macOS)) to see your changes! 76 | 77 | For **iOS**: Hit Cmd ⌘ + R in your iOS Simulator to reload the app and see your changes! 78 | 79 | ## Congratulations! :tada: 80 | 81 | You've successfully run and modified your React Native App. :partying_face: 82 | 83 | ### Now what? 84 | 85 | - If you want to add this new React Native code to an existing application, check out the [Integration guide](https://reactnative.dev/docs/integration-with-existing-apps). 86 | - If you're curious to learn more about React Native, check out the [Introduction to React Native](https://reactnative.dev/docs/getting-started). 87 | 88 | # Troubleshooting 89 | 90 | If you can't get this to work, see the [Troubleshooting](https://reactnative.dev/docs/troubleshooting) page or try disabling Flipper by setting `NO_FLIPPER=1` in your environment. (e.g. `NO_FLIPPER=1 yarn ios`) 91 | 92 | # Learn More 93 | 94 | To learn more about React Native, take a look at the following resources: 95 | 96 | - [React Native Website](https://reactnative.dev) - learn more about React Native. 97 | - [Getting Started](https://reactnative.dev/docs/environment-setup) - an **overview** of React Native and how setup your environment. 98 | - [Learn the Basics](https://reactnative.dev/docs/getting-started) - a **guided tour** of the React Native **basics**. 99 | - [Blog](https://reactnative.dev/blog) - read the latest official React Native **Blog** posts. 100 | - [`@facebook/react-native`](https://github.com/facebook/react-native) - the Open Source; GitHub **repository** for React Native. 101 | -------------------------------------------------------------------------------- /IapExample/__tests__/App.test.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | */ 4 | 5 | import 'react-native'; 6 | import React from 'react'; 7 | import {App} from '../src/App'; 8 | 9 | // Note: import explicitly to use the types shiped with jest. 10 | import {it} from '@jest/globals'; 11 | 12 | // Note: test renderer must be required after react-native. 13 | import renderer from 'react-test-renderer'; 14 | 15 | it('renders correctly', () => { 16 | renderer.create(); 17 | }); 18 | -------------------------------------------------------------------------------- /IapExample/android/app/amazon.sdktester.json: -------------------------------------------------------------------------------- 1 | { 2 | "com.amazon.sample.iap.subscription.mymagazine.month": { 3 | "description":"Monthly Subscription to My Magazine", 4 | "title":"My Magazine", 5 | "itemType":"SUBSCRIPTION", 6 | "price":5.0, 7 | "subscriptionParent":"com.amazon.sample.iap.subscription.mymagazine" 8 | }, 9 | "com.amazon.sample.iap.subscription.mymagazine.quarter": { 10 | "description":"Quarterly Subscription to My Magazine", 11 | "title":"My Magazine", 12 | "itemType":"SUBSCRIPTION", 13 | "price":12.0, 14 | "subscriptionParent":"com.amazon.sample.iap.subscription.mymagazine" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /IapExample/android/app/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyochan/react-native-iap/a0578ecd9e349ac8959c7b3a2019dc4151dcd0a9/IapExample/android/app/debug.keystore -------------------------------------------------------------------------------- /IapExample/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | -------------------------------------------------------------------------------- /IapExample/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /IapExample/android/app/src/debug/java/com/iapexample/ReactNativeFlipper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | *

This source code is licensed under the MIT license found in the LICENSE file in the root 5 | * directory of this source tree. 6 | */ 7 | package com.dooboolab.test; 8 | 9 | import android.content.Context; 10 | import com.facebook.flipper.android.AndroidFlipperClient; 11 | import com.facebook.flipper.android.utils.FlipperUtils; 12 | import com.facebook.flipper.core.FlipperClient; 13 | import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin; 14 | import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin; 15 | import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin; 16 | import com.facebook.flipper.plugins.inspector.DescriptorMapping; 17 | import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin; 18 | import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor; 19 | import com.facebook.flipper.plugins.network.NetworkFlipperPlugin; 20 | import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin; 21 | import com.facebook.react.ReactInstanceEventListener; 22 | import com.facebook.react.ReactInstanceManager; 23 | import com.facebook.react.bridge.ReactContext; 24 | import com.facebook.react.modules.network.NetworkingModule; 25 | import okhttp3.OkHttpClient; 26 | 27 | /** 28 | * Class responsible of loading Flipper inside your React Native application. This is the debug 29 | * flavor of it. Here you can add your own plugins and customize the Flipper setup. 30 | */ 31 | public class ReactNativeFlipper { 32 | public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) { 33 | if (FlipperUtils.shouldEnableFlipper(context)) { 34 | final FlipperClient client = AndroidFlipperClient.getInstance(context); 35 | 36 | client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults())); 37 | client.addPlugin(new DatabasesFlipperPlugin(context)); 38 | client.addPlugin(new SharedPreferencesFlipperPlugin(context)); 39 | client.addPlugin(CrashReporterPlugin.getInstance()); 40 | 41 | NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin(); 42 | NetworkingModule.setCustomClientBuilder( 43 | new NetworkingModule.CustomClientBuilder() { 44 | @Override 45 | public void apply(OkHttpClient.Builder builder) { 46 | builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin)); 47 | } 48 | }); 49 | client.addPlugin(networkFlipperPlugin); 50 | client.start(); 51 | 52 | // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized 53 | // Hence we run if after all native modules have been initialized 54 | ReactContext reactContext = reactInstanceManager.getCurrentReactContext(); 55 | if (reactContext == null) { 56 | reactInstanceManager.addReactInstanceEventListener( 57 | new ReactInstanceEventListener() { 58 | @Override 59 | public void onReactContextInitialized(ReactContext reactContext) { 60 | reactInstanceManager.removeReactInstanceEventListener(this); 61 | reactContext.runOnNativeModulesQueueThread( 62 | new Runnable() { 63 | @Override 64 | public void run() { 65 | client.addPlugin(new FrescoFlipperPlugin()); 66 | } 67 | }); 68 | } 69 | }); 70 | } else { 71 | client.addPlugin(new FrescoFlipperPlugin()); 72 | } 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /IapExample/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /IapExample/android/app/src/main/java/com/iapexample/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.dooboolab.test; 2 | 3 | import com.facebook.react.ReactActivity; 4 | import com.facebook.react.ReactActivityDelegate; 5 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint; 6 | import com.facebook.react.defaults.DefaultReactActivityDelegate; 7 | 8 | public class MainActivity extends ReactActivity { 9 | 10 | /** 11 | * Returns the name of the main component registered from JavaScript. This is used to schedule 12 | * rendering of the component. 13 | */ 14 | @Override 15 | protected String getMainComponentName() { 16 | return "IapExample"; 17 | } 18 | 19 | /** 20 | * Returns the instance of the {@link ReactActivityDelegate}. Here we use a util class {@link 21 | * DefaultReactActivityDelegate} which allows you to easily enable Fabric and Concurrent React 22 | * (aka React 18) with two boolean flags. 23 | */ 24 | @Override 25 | protected ReactActivityDelegate createReactActivityDelegate() { 26 | return new DefaultReactActivityDelegate( 27 | this, 28 | getMainComponentName(), 29 | // If you opted-in for the New Architecture, we enable the Fabric Renderer. 30 | DefaultNewArchitectureEntryPoint.getFabricEnabled()); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /IapExample/android/app/src/main/java/com/iapexample/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.dooboolab.test; 2 | 3 | import android.app.Application; 4 | import com.facebook.react.PackageList; 5 | import com.facebook.react.ReactApplication; 6 | import com.facebook.react.ReactNativeHost; 7 | import com.facebook.react.ReactPackage; 8 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint; 9 | import com.facebook.react.defaults.DefaultReactNativeHost; 10 | import com.facebook.soloader.SoLoader; 11 | import java.util.List; 12 | 13 | public class MainApplication extends Application implements ReactApplication { 14 | 15 | private final ReactNativeHost mReactNativeHost = 16 | new DefaultReactNativeHost(this) { 17 | @Override 18 | public boolean getUseDeveloperSupport() { 19 | return BuildConfig.DEBUG; 20 | } 21 | 22 | @Override 23 | protected List getPackages() { 24 | @SuppressWarnings("UnnecessaryLocalVariable") 25 | List packages = new PackageList(this).getPackages(); 26 | // Packages that cannot be autolinked yet can be added manually here, for example: 27 | // packages.add(new MyReactNativePackage()); 28 | return packages; 29 | } 30 | 31 | @Override 32 | protected String getJSMainModuleName() { 33 | return "index"; 34 | } 35 | 36 | @Override 37 | protected boolean isNewArchEnabled() { 38 | return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED; 39 | } 40 | 41 | @Override 42 | protected Boolean isHermesEnabled() { 43 | return BuildConfig.IS_HERMES_ENABLED; 44 | } 45 | }; 46 | 47 | @Override 48 | public ReactNativeHost getReactNativeHost() { 49 | return mReactNativeHost; 50 | } 51 | 52 | @Override 53 | public void onCreate() { 54 | super.onCreate(); 55 | SoLoader.init(this, /* native exopackage */ false); 56 | if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { 57 | // If you opted-in for the New Architecture, we load the native entry point for this app. 58 | DefaultNewArchitectureEntryPoint.load(); 59 | } 60 | ReactNativeFlipper.initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /IapExample/android/app/src/main/res/drawable/rn_edit_text_material.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 21 | 22 | 23 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /IapExample/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyochan/react-native-iap/a0578ecd9e349ac8959c7b3a2019dc4151dcd0a9/IapExample/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /IapExample/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyochan/react-native-iap/a0578ecd9e349ac8959c7b3a2019dc4151dcd0a9/IapExample/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /IapExample/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyochan/react-native-iap/a0578ecd9e349ac8959c7b3a2019dc4151dcd0a9/IapExample/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /IapExample/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyochan/react-native-iap/a0578ecd9e349ac8959c7b3a2019dc4151dcd0a9/IapExample/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /IapExample/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyochan/react-native-iap/a0578ecd9e349ac8959c7b3a2019dc4151dcd0a9/IapExample/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /IapExample/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyochan/react-native-iap/a0578ecd9e349ac8959c7b3a2019dc4151dcd0a9/IapExample/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /IapExample/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyochan/react-native-iap/a0578ecd9e349ac8959c7b3a2019dc4151dcd0a9/IapExample/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /IapExample/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyochan/react-native-iap/a0578ecd9e349ac8959c7b3a2019dc4151dcd0a9/IapExample/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /IapExample/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyochan/react-native-iap/a0578ecd9e349ac8959c7b3a2019dc4151dcd0a9/IapExample/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /IapExample/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyochan/react-native-iap/a0578ecd9e349ac8959c7b3a2019dc4151dcd0a9/IapExample/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /IapExample/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | IapExample 3 | 4 | -------------------------------------------------------------------------------- /IapExample/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /IapExample/android/app/src/release/java/com/iapexample/ReactNativeFlipper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | *

This source code is licensed under the MIT license found in the LICENSE file in the root 5 | * directory of this source tree. 6 | */ 7 | package com.dooboolab.test; 8 | 9 | import android.content.Context; 10 | import com.facebook.react.ReactInstanceManager; 11 | 12 | /** 13 | * Class responsible of loading Flipper inside your React Native application. This is the release 14 | * flavor of it so it's empty as we don't want to load Flipper. 15 | */ 16 | public class ReactNativeFlipper { 17 | public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) { 18 | // Do nothing as we don't want to initialize Flipper on Release. 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /IapExample/android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext { 5 | buildToolsVersion = "33.0.0" 6 | minSdkVersion = 21 7 | compileSdkVersion = 33 8 | targetSdkVersion = 33 9 | 10 | // We use NDK 23 which has both M1 support and is the side-by-side NDK version from AGP. 11 | ndkVersion = "23.1.7779620" 12 | } 13 | repositories { 14 | google() 15 | mavenCentral() 16 | } 17 | dependencies { 18 | classpath("com.android.tools.build:gradle") 19 | classpath("com.facebook.react:react-native-gradle-plugin") 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /IapExample/android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx512m -XX:MaxMetaspaceSize=256m 13 | org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | # AndroidX package structure to make it clearer which packages are bundled with the 21 | # Android operating system, and which are packaged with your app's APK 22 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 23 | android.useAndroidX=true 24 | # Automatically convert third-party libraries to use AndroidX 25 | android.enableJetifier=true 26 | 27 | # Version of flipper SDK to use with React Native 28 | FLIPPER_VERSION=0.182.0 29 | 30 | # Use this property to specify which architecture you want to build. 31 | # You can also override it from the CLI using 32 | # ./gradlew -PreactNativeArchitectures=x86_64 33 | reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 34 | 35 | # Use this property to enable support to the new architecture. 36 | # This will allow you to use TurboModules and the Fabric render in 37 | # your application. You should enable this flag either if you want 38 | # to write custom TurboModules/Fabric components OR use libraries that 39 | # are providing them. 40 | newArchEnabled=false 41 | 42 | # Use this property to enable or disable the Hermes JS engine. 43 | # If set to false, you will be using JSC instead. 44 | hermesEnabled=true 45 | -------------------------------------------------------------------------------- /IapExample/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyochan/react-native-iap/a0578ecd9e349ac8959c7b3a2019dc4151dcd0a9/IapExample/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /IapExample/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-all.zip 4 | networkTimeout=10000 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /IapExample/android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /IapExample/android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'IapExample' 2 | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) 3 | include ':app' 4 | includeBuild('../node_modules/@react-native/gradle-plugin') 5 | -------------------------------------------------------------------------------- /IapExample/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "IapExample", 3 | "displayName": "IapExample" 4 | } 5 | -------------------------------------------------------------------------------- /IapExample/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:@react-native/babel-preset'], 3 | }; 4 | -------------------------------------------------------------------------------- /IapExample/index.tsx: -------------------------------------------------------------------------------- 1 | import {AppRegistry} from 'react-native'; 2 | 3 | import {App} from './src/App'; 4 | import {name as appName} from './app.json'; 5 | 6 | AppRegistry.registerComponent(appName, () => App); 7 | -------------------------------------------------------------------------------- /IapExample/ios/.xcode.env: -------------------------------------------------------------------------------- 1 | # This `.xcode.env` file is versioned and is used to source the environment 2 | # used when running script phases inside Xcode. 3 | # To customize your local environment, you can create an `.xcode.env.local` 4 | # file that is not versioned. 5 | 6 | # NODE_BINARY variable contains the PATH to the node executable. 7 | # 8 | # Customize the NODE_BINARY variable here. 9 | # For example, to use nvm with brew, add the following line 10 | # . "$(brew --prefix nvm)/nvm.sh" --no-use 11 | export NODE_BINARY=$(command -v node) 12 | -------------------------------------------------------------------------------- /IapExample/ios/Configuration.storekit: -------------------------------------------------------------------------------- 1 | { 2 | "identifier" : "5BB04EE6", 3 | "nonRenewingSubscriptions" : [ 4 | { 5 | "displayPrice" : "9.99", 6 | "familyShareable" : false, 7 | "internalID" : "99AC5E3C", 8 | "localizations" : [ 9 | { 10 | "description" : "", 11 | "displayName" : "", 12 | "locale" : "en_US" 13 | } 14 | ], 15 | "productID" : "com.cooni.sub1000", 16 | "referenceName" : "1000 point subscription", 17 | "type" : "NonRenewingSubscription" 18 | } 19 | ], 20 | "products" : [ 21 | { 22 | "displayPrice" : "0.99", 23 | "familyShareable" : false, 24 | "internalID" : "FF5725DC", 25 | "localizations" : [ 26 | { 27 | "description" : "", 28 | "displayName" : "", 29 | "locale" : "en_US" 30 | } 31 | ], 32 | "productID" : "com.cooni.point1000", 33 | "referenceName" : "1000 points", 34 | "type" : "Consumable" 35 | }, 36 | { 37 | "displayPrice" : "2.99", 38 | "familyShareable" : false, 39 | "internalID" : "47DD16EA", 40 | "localizations" : [ 41 | { 42 | "description" : "", 43 | "displayName" : "", 44 | "locale" : "en_US" 45 | } 46 | ], 47 | "productID" : "com.cooni.point5000", 48 | "referenceName" : "5000 points", 49 | "type" : "NonConsumable" 50 | } 51 | ], 52 | "settings" : { 53 | "_askToBuyEnabled" : false 54 | }, 55 | "subscriptionGroups" : [ 56 | { 57 | "id" : "FBD9754C", 58 | "localizations" : [ 59 | 60 | ], 61 | "name" : "Point subscription group", 62 | "subscriptions" : [ 63 | { 64 | "adHocOffers" : [ 65 | 66 | ], 67 | "codeOffers" : [ 68 | 69 | ], 70 | "displayPrice" : "29.99", 71 | "familyShareable" : false, 72 | "groupNumber" : 1, 73 | "internalID" : "1FFFCCC1", 74 | "introductoryOffer" : { 75 | "internalID" : "5FF46BC8", 76 | "paymentMode" : "free", 77 | "subscriptionPeriod" : "P1W" 78 | }, 79 | "localizations" : [ 80 | { 81 | "description" : "", 82 | "displayName" : "", 83 | "locale" : "en_US" 84 | } 85 | ], 86 | "productID" : "com.cooni.sub5000", 87 | "recurringSubscriptionPeriod" : "P1M", 88 | "referenceName" : "5000 Auto-renewable subscription", 89 | "subscriptionGroupID" : "FBD9754C", 90 | "type" : "RecurringSubscription" 91 | } 92 | ] 93 | } 94 | ], 95 | "subscriptionOffersKeyPair" : { 96 | "id" : "9AB2A8DD", 97 | "privateKey" : "MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgQa0Te5HV/F3M2TlphhcZylYlz2tJrL6Esp6CsYNoTOugCgYIKoZIzj0DAQehRANCAATdiy7Y+lM3aFDlLE0reYd5yF2pzZeQJ6h+9jfMDlg5aDOYXEF0978WMYrHYLOcEbwLxqAUpO1FcLu9VR2Vg98t" 98 | }, 99 | "version" : { 100 | "major" : 1, 101 | "minor" : 2 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /IapExample/ios/IapExample.xcodeproj/xcshareddata/xcschemes/IapExample.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 53 | 55 | 61 | 62 | 63 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /IapExample/ios/IapExample.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /IapExample/ios/IapExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /IapExample/ios/IapExample/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : RCTAppDelegate 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /IapExample/ios/IapExample/AppDelegate.mm: -------------------------------------------------------------------------------- 1 | #import "AppDelegate.h" 2 | 3 | #import 4 | 5 | @implementation AppDelegate 6 | 7 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 8 | { 9 | self.moduleName = @"IapExample"; 10 | // You can add your custom initial props in the dictionary below. 11 | // They will be passed down to the ViewController used by React Native. 12 | self.initialProps = @{}; 13 | 14 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 15 | } 16 | 17 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge 18 | { 19 | #if DEBUG 20 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; 21 | #else 22 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; 23 | #endif 24 | } 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /IapExample/ios/IapExample/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ios-marketing", 45 | "scale" : "1x", 46 | "size" : "1024x1024" 47 | } 48 | ], 49 | "info" : { 50 | "author" : "xcode", 51 | "version" : 1 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /IapExample/ios/IapExample/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /IapExample/ios/IapExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | IapExample 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(MARKETING_VERSION) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(CURRENT_PROJECT_VERSION) 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | NSExceptionDomains 30 | 31 | localhost 32 | 33 | NSExceptionAllowsInsecureHTTPLoads 34 | 35 | 36 | 37 | 38 | NSLocationWhenInUseUsageDescription 39 | 40 | UILaunchStoryboardName 41 | LaunchScreen 42 | UIRequiredDeviceCapabilities 43 | 44 | armv7 45 | 46 | UISupportedInterfaceOrientations 47 | 48 | UIInterfaceOrientationPortrait 49 | UIInterfaceOrientationLandscapeLeft 50 | UIInterfaceOrientationLandscapeRight 51 | 52 | UIViewControllerBasedStatusBarAppearance 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /IapExample/ios/IapExample/PrivacyInfo.xcprivacy: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSPrivacyAccessedAPITypes 6 | 7 | 8 | NSPrivacyAccessedAPIType 9 | NSPrivacyAccessedAPICategoryFileTimestamp 10 | NSPrivacyAccessedAPITypeReasons 11 | 12 | C617.1 13 | 14 | 15 | 16 | NSPrivacyAccessedAPIType 17 | NSPrivacyAccessedAPICategoryUserDefaults 18 | NSPrivacyAccessedAPITypeReasons 19 | 20 | CA92.1 21 | 22 | 23 | 24 | NSPrivacyAccessedAPIType 25 | NSPrivacyAccessedAPICategorySystemBootTime 26 | NSPrivacyAccessedAPITypeReasons 27 | 28 | 35F9.1 29 | 30 | 31 | 32 | NSPrivacyCollectedDataTypes 33 | 34 | NSPrivacyTracking 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /IapExample/ios/IapExample/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | @autoreleasepool { 8 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /IapExample/ios/IapExampleTests/IapExampleTests.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | #import 5 | #import 6 | 7 | #define TIMEOUT_SECONDS 600 8 | #define TEXT_TO_LOOK_FOR @"Welcome to React" 9 | 10 | @interface IapExampleTests : XCTestCase 11 | 12 | @end 13 | 14 | @implementation IapExampleTests 15 | 16 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL (^)(UIView *view))test 17 | { 18 | if (test(view)) { 19 | return YES; 20 | } 21 | for (UIView *subview in [view subviews]) { 22 | if ([self findSubviewInView:subview matching:test]) { 23 | return YES; 24 | } 25 | } 26 | return NO; 27 | } 28 | 29 | - (void)testRendersWelcomeScreen 30 | { 31 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; 32 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; 33 | BOOL foundElement = NO; 34 | 35 | __block NSString *redboxError = nil; 36 | #ifdef DEBUG 37 | RCTSetLogFunction( 38 | ^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { 39 | if (level >= RCTLogLevelError) { 40 | redboxError = message; 41 | } 42 | }); 43 | #endif 44 | 45 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { 46 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 47 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 48 | 49 | foundElement = [self findSubviewInView:vc.view 50 | matching:^BOOL(UIView *view) { 51 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { 52 | return YES; 53 | } 54 | return NO; 55 | }]; 56 | } 57 | 58 | #ifdef DEBUG 59 | RCTSetLogFunction(RCTDefaultLogFunction); 60 | #endif 61 | 62 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); 63 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); 64 | } 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /IapExample/ios/IapExampleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /IapExample/ios/Podfile: -------------------------------------------------------------------------------- 1 | require 'xcodeproj' 2 | 3 | # Resolve react_native_pods.rb with node to allow for hoisting 4 | require Pod::Executable.execute_command('node', ['-p', 5 | 'require.resolve( 6 | "react-native/scripts/react_native_pods.rb", 7 | {paths: [process.argv[1]]}, 8 | )', __dir__]).strip 9 | 10 | project_path = './IapExample.xcodeproj' 11 | project = Xcodeproj::Project.open(project_path) 12 | 13 | # Fetches minimum deployment target version from the project and sets it as the default 14 | config_list = project.root_object.build_configuration_list 15 | debug_config = config_list.build_configurations.find { |config| config.name == 'Debug' } 16 | min_ios_version = debug_config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] 17 | default_min_ios_version = '12.4' 18 | if min_ios_version.nil? || min_ios_version.empty? 19 | puts "IPHONEOS_DEPLOYMENT_TARGET not set at the project level for Debug configuration. Using default value of #{default_min_ios_version}" 20 | min_ios_version = default_min_ios_version 21 | else 22 | puts "Minimum iOS version set to: #{min_ios_version}" 23 | end 24 | platform :ios, min_ios_version || default_min_ios_version 25 | 26 | prepare_react_native_project! 27 | 28 | linkage = ENV['USE_FRAMEWORKS'] 29 | if linkage != nil 30 | Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green 31 | use_frameworks! :linkage => linkage.to_sym 32 | end 33 | 34 | target 'IapExample' do 35 | config = use_native_modules! 36 | 37 | # Flags change depending on the env values. 38 | flags = get_default_flags() 39 | 40 | use_react_native!( 41 | :path => config[:reactNativePath], 42 | # Hermes is now enabled by default. Disable by setting this flag to false. 43 | :hermes_enabled => flags[:hermes_enabled], 44 | :fabric_enabled => flags[:fabric_enabled], 45 | # An absolute path to your application root. 46 | :app_path => "#{Pod::Config.instance.installation_root}/.." 47 | ) 48 | 49 | target 'IapExampleTests' do 50 | inherit! :complete 51 | # Pods for testing 52 | end 53 | 54 | post_install do |installer| 55 | # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202 56 | react_native_post_install( 57 | installer, 58 | config[:reactNativePath], 59 | :mac_catalyst_enabled => false 60 | ) 61 | __apply_Xcode_12_5_M1_post_install_workaround(installer) 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /IapExample/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'react-native', 3 | }; 4 | -------------------------------------------------------------------------------- /IapExample/metro.config.js: -------------------------------------------------------------------------------- 1 | const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config'); 2 | 3 | /** 4 | * Metro configuration 5 | * https://facebook.github.io/metro/docs/configuration 6 | * 7 | * @type {import('metro-config').MetroConfig} 8 | */ 9 | const config = {}; 10 | 11 | module.exports = mergeConfig(getDefaultConfig(__dirname), config); 12 | -------------------------------------------------------------------------------- /IapExample/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "iap-example", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "android:play": "react-native run-android --variant=GooglePlayDebug", 7 | "android:amazon": "react-native run-android --variant=AmazonDebug", 8 | "ios": "react-native run-ios", 9 | "lint": "eslint .", 10 | "start": "react-native start", 11 | "test": "jest", 12 | "pods": "pod-install", 13 | "postinstall": "rm -rf node_modules/react-native-iap/node_modules" 14 | }, 15 | "dependencies": { 16 | "@react-native-community/cli-platform-android": "^12.2.0", 17 | "@react-native-masked-view/masked-view": "^0.3.0", 18 | "@react-navigation/native": "^6.1.9", 19 | "@react-navigation/stack": "^6.3.20", 20 | "react-native-iap": "file:..", 21 | "lodash": "^4.17.21", 22 | "react": "18.2.0", 23 | "react-native": "0.72.17", 24 | "react-native-gesture-handler": "^2.14.0", 25 | "react-native-safe-area-context": "^4.7.4", 26 | "react-native-screens": "^3.27.0" 27 | }, 28 | "devDependencies": { 29 | "@babel/core": "^7.20.0", 30 | "@babel/preset-env": "^7.20.0", 31 | "@babel/runtime": "^7.26.10", 32 | "@react-native/eslint-config": "^0.72.2", 33 | "@react-native/metro-config": "^0.72.11", 34 | "@tsconfig/react-native": "^3.0.0", 35 | "@types/lodash": "^4.14.202", 36 | "@types/react": "^18.0.24", 37 | "@types/react-test-renderer": "^18.0.0", 38 | "babel-jest": "^29.2.1", 39 | "eslint": "^8.19.0", 40 | "jest": "^29.2.1", 41 | "metro-react-native-babel-preset": "0.76.8", 42 | "pod-install": "^0.1.39", 43 | "prettier": "^2.4.1", 44 | "react-test-renderer": "18.2.0", 45 | "typescript": "4.8.4" 46 | }, 47 | "engines": { 48 | "node": ">=16" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /IapExample/react-native.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | dependencies: { 5 | 'react-native-iap': { 6 | root: path.join(__dirname, '..'), 7 | }, 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /IapExample/src/App.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {setup} from 'react-native-iap'; 3 | import {NavigationContainer} from '@react-navigation/native'; 4 | 5 | import {StackNavigator} from './navigators'; 6 | setup({storekitMode: 'STOREKIT2_MODE'}); 7 | 8 | export const App = () => ( 9 | 10 | 11 | 12 | ); 13 | -------------------------------------------------------------------------------- /IapExample/src/components/Box.tsx: -------------------------------------------------------------------------------- 1 | import React, {ReactNode} from 'react'; 2 | import {View} from 'react-native'; 3 | 4 | import {theme} from '../utils'; 5 | 6 | interface BoxProps { 7 | children: ReactNode; 8 | } 9 | 10 | export const Box = ({children}: BoxProps) => ( 11 | {children} 12 | ); 13 | -------------------------------------------------------------------------------- /IapExample/src/components/Button.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Button as RNButton, StyleSheet, View} from 'react-native'; 3 | 4 | import {borderRadius, colors} from '../utils'; 5 | 6 | interface ButtonProps { 7 | title: string; 8 | 9 | onPress(): void; 10 | } 11 | 12 | export const Button = ({title, onPress}: ButtonProps) => ( 13 | 14 | 15 | 16 | ); 17 | 18 | const styles = StyleSheet.create({ 19 | button: { 20 | backgroundColor: colors.gray100, 21 | borderRadius: borderRadius - 2, 22 | }, 23 | }); 24 | -------------------------------------------------------------------------------- /IapExample/src/components/Heading.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {StyleSheet, Text, View} from 'react-native'; 3 | 4 | import {borderRadius, colors, theme} from '../utils'; 5 | 6 | interface HeadingProps { 7 | copy: string; 8 | label?: string; 9 | } 10 | 11 | export const Heading = ({copy, label}: HeadingProps) => ( 12 | 13 | {copy} 14 | {label && {label}} 15 | 16 | ); 17 | 18 | const styles = StyleSheet.create({ 19 | heading: { 20 | flexDirection: 'row', 21 | justifyContent: 'space-between', 22 | paddingVertical: 8, 23 | paddingHorizontal: 12, 24 | backgroundColor: colors.gray100, 25 | borderRadius: borderRadius - 2, 26 | }, 27 | 28 | label: { 29 | ...theme.L1, 30 | color: colors.gray600, 31 | }, 32 | }); 33 | -------------------------------------------------------------------------------- /IapExample/src/components/Row.tsx: -------------------------------------------------------------------------------- 1 | import React, {ReactNode} from 'react'; 2 | import {StyleSheet, Text, View, ViewStyle} from 'react-native'; 3 | import camelCase from 'lodash/camelCase'; 4 | 5 | import {theme} from '../utils'; 6 | 7 | interface RowField { 8 | label: string; 9 | value: string; 10 | } 11 | 12 | interface RowProps { 13 | children?: ReactNode; 14 | fields: RowField[]; 15 | flexDirection?: ViewStyle['flexDirection']; 16 | isLast?: boolean; 17 | } 18 | 19 | export const Row = ({ 20 | children, 21 | fields, 22 | flexDirection = 'row', 23 | isLast, 24 | }: RowProps) => ( 25 | 26 | 27 | {fields.map((field, index) => ( 28 | 31 | {field.label} 32 | {field.value} 33 | 34 | ))} 35 | 36 | 37 | {children} 38 | 39 | ); 40 | 41 | const styles = StyleSheet.create({ 42 | row: { 43 | marginBottom: 10, 44 | }, 45 | 46 | rowLast: { 47 | marginBottom: 0, 48 | }, 49 | }); 50 | -------------------------------------------------------------------------------- /IapExample/src/components/State.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Text} from 'react-native'; 3 | 4 | import {theme} from '../utils'; 5 | 6 | import {Box} from './Box'; 7 | 8 | interface StateProps { 9 | connected: boolean; 10 | storekit2?: boolean; 11 | } 12 | 13 | export const State = ({connected, storekit2}: StateProps) => { 14 | const stateText = 15 | (connected ? 'connected' : 'not connected') + 16 | (storekit2 ? ' | Storekit 2' : ''); 17 | return ( 18 | 19 | State 20 | {stateText} 21 | 22 | ); 23 | }; 24 | -------------------------------------------------------------------------------- /IapExample/src/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Box'; 2 | export * from './Button'; 3 | export * from './Heading'; 4 | export * from './Row'; 5 | export * from './State'; 6 | -------------------------------------------------------------------------------- /IapExample/src/navigators/StackNavigator.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {withIAPContext} from 'react-native-iap'; 3 | import {createStackNavigator} from '@react-navigation/stack'; 4 | 5 | import { 6 | ClassSetup, 7 | Examples, 8 | Products, 9 | PurchaseHistory, 10 | Subscriptions, 11 | } from '../screens'; 12 | import {AvailablePurchases} from '../screens/AvailablePurchases'; 13 | 14 | export const examples = [ 15 | { 16 | name: 'Products', 17 | label: 'With success and error listeners.', 18 | component: withIAPContext(Products), 19 | section: 'Context', 20 | color: '#47d371', 21 | emoji: '💵', 22 | }, 23 | { 24 | name: 'Subscriptions', 25 | component: withIAPContext(Subscriptions), 26 | section: 'Context', 27 | color: '#cebf38', 28 | emoji: '💳', 29 | }, 30 | { 31 | name: 'PurchaseHistory', 32 | component: withIAPContext(PurchaseHistory), 33 | section: 'Context', 34 | color: '#c241b3', 35 | emoji: '📄', 36 | }, 37 | { 38 | name: 'AvailablePurchases', 39 | component: withIAPContext(AvailablePurchases), 40 | section: 'Context', 41 | color: '#475ed3', 42 | emoji: '🧾', 43 | }, 44 | { 45 | name: 'ClassSetup', 46 | component: ClassSetup, 47 | section: 'Others', 48 | color: '#b947d3', 49 | emoji: '', 50 | }, 51 | ] as const; 52 | 53 | export type Screens = { 54 | Examples: undefined; 55 | Products: undefined; 56 | Subscriptions: undefined; 57 | PurchaseHistory: undefined; 58 | AvailablePurchases: undefined; 59 | Listeners: undefined; 60 | ClassSetup: undefined; 61 | }; 62 | 63 | const Stack = createStackNavigator(); 64 | 65 | export const StackNavigator = () => ( 66 | 67 | 68 | 69 | {examples.map(({name, component}) => ( 70 | 79 | ))} 80 | 81 | ); 82 | -------------------------------------------------------------------------------- /IapExample/src/navigators/index.ts: -------------------------------------------------------------------------------- 1 | export * from './StackNavigator'; 2 | -------------------------------------------------------------------------------- /IapExample/src/screens/AvailablePurchases.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {ScrollView, StyleSheet, View} from 'react-native'; 3 | import {useIAP} from 'react-native-iap'; 4 | 5 | import {Box, Button, Heading, Row, State} from '../components'; 6 | import {contentContainerStyle, errorLog} from '../utils'; 7 | 8 | export const AvailablePurchases = () => { 9 | const {connected, availablePurchases, getAvailablePurchases} = useIAP(); 10 | 11 | const handleGetAvailablePurchases = async () => { 12 | try { 13 | await getAvailablePurchases(); 14 | } catch (error) { 15 | errorLog({message: 'handleGetAvailablePurchases', error}); 16 | } 17 | }; 18 | 19 | return ( 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | {availablePurchases.map((availablePurchase, index) => ( 28 | 38 | ))} 39 | 40 | 41 |