diff --git a/OAT.xml b/OAT.xml index e6853d4d5cb369825a92b3423ecc851355d2a781..6c97f87cc99d3191bec3ea358eb97e1856f8ce62 100644 --- a/OAT.xml +++ b/OAT.xml @@ -59,6 +59,22 @@ name="Hunan OpenValley Digital Industry Development Co., Ltd." path=".*" rule="may" + type="copyright"/> + + - - + + @@ -286,6 +302,21 @@ + + + + + + + + + + + + + + + diff --git a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md index 652ebc3b46e50d9234e0b6964e6a2e0beea2d3ac..c9dd5bd0fb1503206e05ea62a937175c56abbf46 100644 --- a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md @@ -1,3 +1,7 @@ +## 3.1.11 + +* Updates documentation reference of `finishPurchase` to `completePurchase`. + ## 3.1.10 * Updates example code for current versions of Flutter. diff --git a/packages/in_app_purchase/in_app_purchase/README.md b/packages/in_app_purchase/in_app_purchase/README.md index 59cb2c2dd9a8af2c90092cc2cd8ea67ccc6519e4..f33c4d30cf11ed8f07136b4a7cb1a3f3192c04f8 100644 --- a/packages/in_app_purchase/in_app_purchase/README.md +++ b/packages/in_app_purchase/in_app_purchase/README.md @@ -5,6 +5,23 @@ A storefront-independent API for purchases in Flutter apps. This plugin supports in-app purchases (_IAP_) through an _underlying store_, which can be the App Store (on iOS and macOS) or Google Play (on Android). + + +#### ohos ˵ + +ohos ƽ̨Ŀǰӿڣ + +* purchaseStream +* isAvailable +* queryProductDetails +* buyNonConsumable +* buyConsumable +* completePurchase + +Ŀǰ֧ûӦóдָֻ֧̣֧ȹܣδ + + + | | Android | iOS | macOS | |-------------|---------|-------|--------| | **Support** | SDK 16+ | 11.0+ | 10.15+ | diff --git a/packages/in_app_purchase/in_app_purchase/example/lib/main.dart b/packages/in_app_purchase/in_app_purchase/example/lib/main.dart index 5afb52ac99ea2b5045158ddad1ff54f6f480fbb4..c60f060c1b04f129282a8c497a3217cbdbb52abd 100644 --- a/packages/in_app_purchase/in_app_purchase/example/lib/main.dart +++ b/packages/in_app_purchase/in_app_purchase/example/lib/main.dart @@ -92,6 +92,7 @@ class _MyAppState extends State<_MyApp> { final ProductDetailsResponse productDetailResponse = await _inAppPurchase.queryProductDetails(_kProductIds.toSet()); + var productDetails = productDetailResponse.productDetails; if (productDetailResponse.error != null) { setState(() { _queryProductError = productDetailResponse.error!.message; @@ -149,13 +150,18 @@ class _MyAppState extends State<_MyApp> { if (_queryProductError == null) { stack.add( ListView( - children: [ - _buildConnectionCheckTile(), - _buildProductList(), - _buildConsumableBox(), - _buildRestoreButton(), - ], - ), + children: Platform.operatingSystem == 'ohos' + ? [ + _buildConnectionCheckTile(), + _buildProductList(), + _buildConsumableBox(), + ] + : [ + _buildConnectionCheckTile(), + _buildProductList(), + _buildConsumableBox(), + _buildRestoreButton(), + ]), ); } else { stack.add(Center( @@ -164,7 +170,7 @@ class _MyAppState extends State<_MyApp> { } if (_purchasePending) { stack.add( - const Stack( + Stack( children: [ Opacity( opacity: 0.3, @@ -259,10 +265,12 @@ class _MyAppState extends State<_MyApp> { subtitle: Text( productDetails.description, ), - trailing: previousPurchase != null && Platform.isIOS - ? IconButton( - onPressed: () => confirmPriceChange(context), - icon: const Icon(Icons.upgrade)) + trailing: previousPurchase != null + ? Platform.isIOS + ? IconButton( + onPressed: () => confirmPriceChange(context), + icon: const Icon(Icons.upgrade)) + : SizedBox.shrink() : TextButton( style: TextButton.styleFrom( backgroundColor: Colors.green[800], diff --git a/packages/in_app_purchase/in_app_purchase/example/ohos/.gitignore b/packages/in_app_purchase/in_app_purchase/example/ohos/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..6ca13b3170eec5dd5ac5ad7f1c4dd0118845f473 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase/example/ohos/.gitignore @@ -0,0 +1,19 @@ +/node_modules +/oh_modules +/local.properties +/.idea +**/build +/.hvigor +.cxx +/.clangd +/.clang-format +/.clang-tidy +**/.test +*.har +**/BuildProfile.ets +**/oh-package-lock.json5 + +**/src/main/resources/rawfile/flutter_assets/ +**/libs/arm64-v8a/libapp.so +**/libs/arm64-v8a/libflutter.so +**/libs/arm64-v8a/libvmservice_snapshot.so diff --git a/packages/in_app_purchase/in_app_purchase/example/ohos/AppScope/app.json5 b/packages/in_app_purchase/in_app_purchase/example/ohos/AppScope/app.json5 new file mode 100644 index 0000000000000000000000000000000000000000..a13c8bfdc16a34697a1ce5c7dc8e207275149473 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase/example/ohos/AppScope/app.json5 @@ -0,0 +1,10 @@ +{ + "app": { + "bundleName": "com.example.in_app_purchase_ohos", + "vendor": "example", + "versionCode": 1000000, + "versionName": "1.0.0", + "icon": "$media:app_icon", + "label": "$string:app_name" + } +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase/example/ohos/AppScope/resources/base/element/string.json b/packages/in_app_purchase/in_app_purchase/example/ohos/AppScope/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..810f4a362c1d177309eec4f2efe5cac2f4558c28 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase/example/ohos/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "example" + } + ] +} diff --git a/packages/in_app_purchase/in_app_purchase/example/ohos/AppScope/resources/base/media/app_icon.png b/packages/in_app_purchase/in_app_purchase/example/ohos/AppScope/resources/base/media/app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c Binary files /dev/null and b/packages/in_app_purchase/in_app_purchase/example/ohos/AppScope/resources/base/media/app_icon.png differ diff --git a/packages/in_app_purchase/in_app_purchase/example/ohos/build-profile.json5 b/packages/in_app_purchase/in_app_purchase/example/ohos/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..1d12140d202702d7c73d64f1b291fe5c45a660ce --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase/example/ohos/build-profile.json5 @@ -0,0 +1,27 @@ +{ + "app": { + "signingConfigs": [], + "products": [ + { + "name": "default", + "signingConfig": "default", + "compatibleSdkVersion": "5.0.0(12)", + "runtimeOS": "HarmonyOS" + } + ] + }, + "modules": [ + { + "name": "entry", + "srcPath": "./entry", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase/example/ohos/entry/.gitignore b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..2795a1c5b1fe53659dd1b71d90ba0592eaf7e043 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/.gitignore @@ -0,0 +1,7 @@ + +/node_modules +/oh_modules +/.preview +/build +/.cxx +/.test \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase/example/ohos/entry/build-profile.json5 b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..633d360fbc91a3186a23b66ab71b27e5618944cb --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/build-profile.json5 @@ -0,0 +1,29 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +{ + "apiType": 'stageMode', + "buildOption": { + }, + "targets": [ + { + "name": "default", + "runtimeOS": "HarmonyOS" + }, + { + "name": "ohosTest", + } + ] +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase/example/ohos/entry/hvigorfile.ts b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..894fc15c6b793f085e6c8506e43d719af658e8ff --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/hvigorfile.ts @@ -0,0 +1,17 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently. +export { hapTasks } from '@ohos/hvigor-ohos-plugin'; diff --git a/packages/in_app_purchase/in_app_purchase/example/ohos/entry/oh-package.json5 b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..ebe795339873776bf5a6f746d1f6d1351f208c1f --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/oh-package.json5 @@ -0,0 +1,12 @@ +{ + "name": "entry", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": { + "in_app_purchase_ohos": "file:../har/in_app_purchase_ohos.har", + "integration_test": "file:../har/integration_test.har" + } +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/main/ets/entryability/EntryAbility.ets b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/main/ets/entryability/EntryAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..8bc48be8773196f34cccb15cf517f87f5c6b94d2 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/main/ets/entryability/EntryAbility.ets @@ -0,0 +1,24 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import { FlutterAbility, FlutterEngine } from '@ohos/flutter_ohos'; +import { GeneratedPluginRegistrant } from '../plugins/GeneratedPluginRegistrant'; + +export default class EntryAbility extends FlutterAbility { + configureFlutterEngine(flutterEngine: FlutterEngine) { + super.configureFlutterEngine(flutterEngine) + GeneratedPluginRegistrant.registerWith(flutterEngine) + } +} diff --git a/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/main/ets/pages/Index.ets b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/main/ets/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..1125f9fdd95f4310a182c1c9e3680f37f73686c9 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/main/ets/pages/Index.ets @@ -0,0 +1,38 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import common from '@ohos.app.ability.common'; +import { FlutterPage } from '@ohos/flutter_ohos' + +let storage = LocalStorage.getShared() +const EVENT_BACK_PRESS = 'EVENT_BACK_PRESS' + +@Entry(storage) +@Component +struct Index { + private context = getContext(this) as common.UIAbilityContext + @LocalStorageLink('viewId') viewId: string = ""; + + build() { + Column() { + FlutterPage({ viewId: this.viewId }) + } + } + + onBackPress(): boolean { + this.context.eventHub.emit(EVENT_BACK_PRESS) + return true + } +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/main/module.json5 b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..7bbf78b18f39991b1404061c7437538c7d532bb7 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/main/module.json5 @@ -0,0 +1,53 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +{ + "module": { + "name": "entry", + "type": "entry", + "description": "$string:module_desc", + "mainElement": "EntryAbility", + "deviceTypes": [ + "phone" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "abilities": [ + { + "name": "EntryAbility", + "srcEntry": "./ets/entryability/EntryAbility.ets", + "description": "$string:EntryAbility_desc", + "icon": "$media:icon", + "label": "$string:EntryAbility_label", + "startWindowIcon": "$media:icon", + "startWindowBackground": "$color:start_window_background", + "exported": true, + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ] + } + ], + "requestPermissions": [ + {"name" : "ohos.permission.INTERNET"}, + ] + } +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/main/resources/base/element/color.json b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..3c712962da3c2751c2b9ddb53559afcbd2b54a02 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/main/resources/base/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#FFFFFF" + } + ] +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/main/resources/base/element/string.json b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..67e0f4ff4ac762d1714f6e215c6636a4ad3d620e --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/main/resources/base/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "example" + } + ] +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/main/resources/base/media/icon.png b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/main/resources/base/media/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c Binary files /dev/null and b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/main/resources/base/media/icon.png differ diff --git a/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/main/resources/base/profile/main_pages.json b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..1898d94f58d6128ab712be2c68acc7c98e9ab9ce --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "pages/Index" + ] +} diff --git a/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/main/resources/en_US/element/string.json b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/main/resources/en_US/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..67e0f4ff4ac762d1714f6e215c6636a4ad3d620e --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/main/resources/en_US/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "example" + } + ] +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/main/resources/zh_CN/element/string.json b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/main/resources/zh_CN/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..601e2b5a1c273aa04920b126e3ab715a4450e58f --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "模块描述" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "example" + } + ] +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/ohosTest/ets/test/Ability.test.ets b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/ohosTest/ets/test/Ability.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..25d4c71ff3cd584f5d64f6f8c0ac864928c234c4 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/ohosTest/ets/test/Ability.test.ets @@ -0,0 +1,50 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import hilog from '@ohos.hilog'; +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium' + +export default function abilityTest() { + describe('ActsAbilityTest', function () { + // Defines a test suite. Two parameters are supported: test suite name and test suite function. + beforeAll(function () { + // Presets an action, which is performed only once before all test cases of the test suite start. + // This API supports only one parameter: preset action function. + }) + beforeEach(function () { + // Presets an action, which is performed before each unit test case starts. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: preset action function. + }) + afterEach(function () { + // Presets a clear action, which is performed after each unit test case ends. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: clear action function. + }) + afterAll(function () { + // Presets a clear action, which is performed after all test cases of the test suite end. + // This API supports only one parameter: clear action function. + }) + it('assertContain',0, function () { + // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. + hilog.info(0x0000, 'testTag', '%{public}s', 'it begin'); + let a = 'abc' + let b = 'b' + // Defines a variety of assertion methods, which are used to declare expected boolean conditions. + expect(a).assertContain(b) + expect(a).assertEqual(a) + }) + }) +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/ohosTest/ets/test/List.test.ets b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/ohosTest/ets/test/List.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..f4140030e65d20df6af30a6bf51e464dea8f8aa6 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/ohosTest/ets/test/List.test.ets @@ -0,0 +1,20 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import abilityTest from './Ability.test' + +export default function testsuite() { + abilityTest() +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/ohosTest/ets/testability/TestAbility.ets b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/ohosTest/ets/testability/TestAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..4ca645e6013cfce8e7dbb728313cb8840c4da660 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/ohosTest/ets/testability/TestAbility.ets @@ -0,0 +1,63 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import UIAbility from '@ohos.app.ability.UIAbility'; +import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry'; +import hilog from '@ohos.hilog'; +import { Hypium } from '@ohos/hypium'; +import testsuite from '../test/List.test'; +import window from '@ohos.window'; + +export default class TestAbility extends UIAbility { + onCreate(want, launchParam) { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onCreate'); + hilog.info(0x0000, 'testTag', '%{public}s', 'want param:' + JSON.stringify(want) ?? ''); + hilog.info(0x0000, 'testTag', '%{public}s', 'launchParam:'+ JSON.stringify(launchParam) ?? ''); + var abilityDelegator: any + abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator() + var abilityDelegatorArguments: any + abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments() + hilog.info(0x0000, 'testTag', '%{public}s', 'start run testcase!!!'); + Hypium.hypiumTest(abilityDelegator, abilityDelegatorArguments, testsuite) + } + + onDestroy() { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onDestroy'); + } + + onWindowStageCreate(windowStage: window.WindowStage) { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageCreate'); + windowStage.loadContent('testability/pages/Index', (err, data) => { + if (err.code) { + hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); + return; + } + hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', + JSON.stringify(data) ?? ''); + }); + } + + onWindowStageDestroy() { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageDestroy'); + } + + onForeground() { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onForeground'); + } + + onBackground() { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onBackground'); + } +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/ohosTest/ets/testability/pages/Index.ets b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/ohosTest/ets/testability/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..cef0447cd2f137ef82d223ead2e156808878ab90 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/ohosTest/ets/testability/pages/Index.ets @@ -0,0 +1,49 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import hilog from '@ohos.hilog'; + +@Entry +@Component +struct Index { + aboutToAppear() { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility index aboutToAppear'); + } + @State message: string = 'Hello World' + build() { + Row() { + Column() { + Text(this.message) + .fontSize(50) + .fontWeight(FontWeight.Bold) + Button() { + Text('next page') + .fontSize(20) + .fontWeight(FontWeight.Bold) + }.type(ButtonType.Capsule) + .margin({ + top: 20 + }) + .backgroundColor('#0D9FFB') + .width('35%') + .height('5%') + .onClick(()=>{ + }) + } + .width('100%') + } + .height('100%') + } + } \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts new file mode 100644 index 0000000000000000000000000000000000000000..1def08f2e9dcbfa3454a07b7a3b82b173bb90d02 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts @@ -0,0 +1,64 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import hilog from '@ohos.hilog'; +import TestRunner from '@ohos.application.testRunner'; +import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry'; + +var abilityDelegator = undefined +var abilityDelegatorArguments = undefined + +async function onAbilityCreateCallback() { + hilog.info(0x0000, 'testTag', '%{public}s', 'onAbilityCreateCallback'); +} + +async function addAbilityMonitorCallback(err: any) { + hilog.info(0x0000, 'testTag', 'addAbilityMonitorCallback : %{public}s', JSON.stringify(err) ?? ''); +} + +export default class OpenHarmonyTestRunner implements TestRunner { + constructor() { + } + + onPrepare() { + hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner OnPrepare '); + } + + async onRun() { + hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun run'); + abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments() + abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator() + var testAbilityName = abilityDelegatorArguments.bundleName + '.TestAbility' + let lMonitor = { + abilityName: testAbilityName, + onAbilityCreate: onAbilityCreateCallback, + }; + abilityDelegator.addAbilityMonitor(lMonitor, addAbilityMonitorCallback) + var cmd = 'aa start -d 0 -a TestAbility' + ' -b ' + abilityDelegatorArguments.bundleName + var debug = abilityDelegatorArguments.parameters['-D'] + if (debug == 'true') + { + cmd += ' -D' + } + hilog.info(0x0000, 'testTag', 'cmd : %{public}s', cmd); + abilityDelegator.executeShellCommand(cmd, + (err: any, d: any) => { + hilog.info(0x0000, 'testTag', 'executeShellCommand : err : %{public}s', JSON.stringify(err) ?? ''); + hilog.info(0x0000, 'testTag', 'executeShellCommand : data : %{public}s', d.stdResult ?? ''); + hilog.info(0x0000, 'testTag', 'executeShellCommand : data : %{public}s', d.exitCode ?? ''); + }) + hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun end'); + } +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/ohosTest/module.json5 b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/ohosTest/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..fab77ce2e0c61e3ad010bab5b27ccbd15f9a8c96 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/ohosTest/module.json5 @@ -0,0 +1,51 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +{ + "module": { + "name": "entry_test", + "type": "feature", + "description": "$string:module_test_desc", + "mainElement": "TestAbility", + "deviceTypes": [ + "phone" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:test_pages", + "abilities": [ + { + "name": "TestAbility", + "srcEntry": "./ets/testability/TestAbility.ets", + "description": "$string:TestAbility_desc", + "icon": "$media:icon", + "label": "$string:TestAbility_label", + "exported": true, + "startWindowIcon": "$media:icon", + "startWindowBackground": "$color:start_window_background", + "skills": [ + { + "actions": [ + "action.system.home" + ], + "entities": [ + "entity.system.home" + ] + } + ] + } + ] + } +} diff --git a/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/ohosTest/resources/base/element/color.json b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/ohosTest/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..3c712962da3c2751c2b9ddb53559afcbd2b54a02 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/ohosTest/resources/base/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#FFFFFF" + } + ] +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/ohosTest/resources/base/element/string.json b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/ohosTest/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..65d8fa5a7cf54aa3943dcd0214f58d1771bc1f6c --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/ohosTest/resources/base/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_test_desc", + "value": "test ability description" + }, + { + "name": "TestAbility_desc", + "value": "the test ability" + }, + { + "name": "TestAbility_label", + "value": "test label" + } + ] +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/ohosTest/resources/base/media/icon.png b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/ohosTest/resources/base/media/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c Binary files /dev/null and b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/ohosTest/resources/base/media/icon.png differ diff --git a/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/ohosTest/resources/base/profile/test_pages.json b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/ohosTest/resources/base/profile/test_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..b7e7343cacb32ce982a45e76daad86e435e054fe --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase/example/ohos/entry/src/ohosTest/resources/base/profile/test_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "testability/pages/Index" + ] +} diff --git a/packages/in_app_purchase/in_app_purchase/example/ohos/hvigor/hvigor-config.json5 b/packages/in_app_purchase/in_app_purchase/example/ohos/hvigor/hvigor-config.json5 new file mode 100644 index 0000000000000000000000000000000000000000..541ba35711b75986f9295410ee38fdb8f2572878 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase/example/ohos/hvigor/hvigor-config.json5 @@ -0,0 +1,20 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +{ + "modelVersion": "5.0.0", + "dependencies": { + } +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase/example/ohos/hvigorfile.ts b/packages/in_app_purchase/in_app_purchase/example/ohos/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..8f2d2aafe6d6a3a71a9944ebd0c91fbc308ac9d1 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase/example/ohos/hvigorfile.ts @@ -0,0 +1,21 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import { appTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase/example/ohos/oh-package.json5 b/packages/in_app_purchase/in_app_purchase/example/ohos/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..e955b929b0dc04a27550d0a075407cf140f19b78 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase/example/ohos/oh-package.json5 @@ -0,0 +1,21 @@ +{ + "modelVersion": "5.0.0", + "name": "example", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": { + "@ohos/flutter_ohos": "file:./har/flutter.har" + }, + "devDependencies": { + "@ohos/hypium": "1.0.6" + }, + "overrides": { + "@ohos/flutter_ohos": "file:./har/flutter.har", + "in_app_purchase_ohos": "file:./har/in_app_purchase_ohos.har", + "integration_test": "file:./har/integration_test.har", + "@ohos/flutter_module": "file:./entry" + } +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase/example/pubspec.yaml b/packages/in_app_purchase/in_app_purchase/example/pubspec.yaml index fcb72375f256b699aff61e72b010ceff9a533945..edc13ff9b20771e6df001ef96ed9b61710b6f71a 100644 --- a/packages/in_app_purchase/in_app_purchase/example/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase/example/pubspec.yaml @@ -18,7 +18,14 @@ dependencies: path: ../ in_app_purchase_android: ^0.3.0 in_app_purchase_storekit: ^0.3.4 - shared_preferences: ^2.0.0 + shared_preferences: + git: + url: https://gitee.com/openharmony-sig/flutter_packages.git + path: packages/shared_preferences/shared_preferences + +dependency_overrides: + in_app_purchase_ohos: + path: ../../in_app_purchase_ohos dev_dependencies: flutter_test: diff --git a/packages/in_app_purchase/in_app_purchase/lib/in_app_purchase.dart b/packages/in_app_purchase/in_app_purchase/lib/in_app_purchase.dart index 64e0095dcbb7c47bd95769627a3491390e8366ae..2bd431449f717ada1a9b69a08a956285f18bff81 100644 --- a/packages/in_app_purchase/in_app_purchase/lib/in_app_purchase.dart +++ b/packages/in_app_purchase/in_app_purchase/lib/in_app_purchase.dart @@ -6,6 +6,7 @@ import 'package:flutter/foundation.dart'; import 'package:in_app_purchase_android/in_app_purchase_android.dart'; import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart'; import 'package:in_app_purchase_storekit/in_app_purchase_storekit.dart'; +import 'package:in_app_purchase_ohos/in_app_purchase_ohos.dart'; export 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart' show @@ -37,6 +38,8 @@ class InAppPurchase implements InAppPurchasePlatformAdditionProvider { } else if (defaultTargetPlatform == TargetPlatform.iOS || defaultTargetPlatform == TargetPlatform.macOS) { InAppPurchaseStoreKitPlatform.registerPlatform(); + } else if (defaultTargetPlatform == TargetPlatform.ohos) { + InAppPurchaseOhosPlatform.registerPlatform(); } _instance = InAppPurchase._(); @@ -195,7 +198,7 @@ class InAppPurchase implements InAppPurchasePlatformAdditionProvider { /// Restored purchases are delivered through the [purchaseStream] with a /// status of [PurchaseStatus.restored]. You should listen for these purchases, /// validate their receipts, deliver the content and mark the purchase complete - /// by calling the [finishPurchase] method for each purchase. + /// by calling the [completePurchase] method for each purchase. /// /// This does not return consumed products. If you want to restore unused /// consumable products, you need to persist consumable product information diff --git a/packages/in_app_purchase/in_app_purchase/pubspec.yaml b/packages/in_app_purchase/in_app_purchase/pubspec.yaml index ba95a32125a59e60f5e5ba15b104bb96977669ad..2c30df6651ddd137ef8d4f43052ff0b9a35f628e 100644 --- a/packages/in_app_purchase/in_app_purchase/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase description: A Flutter plugin for in-app purchases. Exposes APIs for making in-app purchases through the App Store and Google Play. repository: https://github.com/flutter/packages/tree/main/packages/in_app_purchase/in_app_purchase issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 3.1.10 +version: 3.1.11 environment: sdk: ">=2.19.0 <4.0.0" @@ -13,6 +13,8 @@ flutter: platforms: android: default_package: in_app_purchase_android + ohos: + default_package: in_app_purchase_ohos ios: default_package: in_app_purchase_storekit macos: @@ -22,6 +24,8 @@ dependencies: flutter: sdk: flutter in_app_purchase_android: ^0.3.0 + in_app_purchase_ohos: + path: ../in_app_purchase_ohos/ in_app_purchase_platform_interface: ^1.0.0 in_app_purchase_storekit: ^0.3.4 diff --git a/packages/in_app_purchase/in_app_purchase_ohos/AUTHORS b/packages/in_app_purchase/in_app_purchase_ohos/AUTHORS new file mode 100644 index 0000000000000000000000000000000000000000..78f9e5ad9f6b842b45e9fd070a44e042d6c7b6a9 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/AUTHORS @@ -0,0 +1,67 @@ +# Below is a list of people and organizations that have contributed +# to the Flutter project. Names should be added to the list like so: +# +# Name/Organization + +Google Inc. +The Chromium Authors +German Saprykin +Benjamin Sauer +larsenthomasj@gmail.com +Ali Bitek +Pol Batlló +Anatoly Pulyaevskiy +Hayden Flinner +Stefano Rodriguez +Salvatore Giordano +Brian Armstrong +Paul DeMarco +Fabricio Nogueira +Simon Lightfoot +Ashton Thomas +Thomas Danner +Diego Velásquez +Hajime Nakamura +Tuyển Vũ Xuân +Miguel Ruivo +Sarthak Verma +Mike Diarmid +Invertase +Elliot Hesp +Vince Varga +Aawaz Gyawali +EUI Limited +Katarina Sheremet +Thomas Stockx +Sarbagya Dhaubanjar +Ozkan Eksi +Rishab Nayak +ko2ic +Jonathan Younger +Jose Sanchez +Debkanchan Samadder +Audrius Karosevicius +Lukasz Piliszczuk +SoundReply Solutions GmbH +Rafal Wachol +Pau Picas +Christian Weder +Alexandru Tuca +Christian Weder +Rhodes Davis Jr. +Luigi Agosti +Quentin Le Guennec +Koushik Ravikumar +Nissim Dsilva +Giancarlo Rocha +Ryo Miyake +Théo Champion +Kazuki Yamaguchi +Eitan Schwartz +Chris Rutkowski +Juan Alvarez +Aleksandr Yurkovskiy +Anton Borries +Alex Li +Rahul Raj <64.rahulraj@gmail.com> +Maurits van Beusekom diff --git a/packages/in_app_purchase/in_app_purchase_ohos/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_ohos/CHANGELOG.md new file mode 100644 index 0000000000000000000000000000000000000000..4fac17c4689aeb69433e4c406db2da67aa1fa4b3 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.1.0 + +* Initial open-source release. diff --git a/packages/in_app_purchase/in_app_purchase_ohos/LICENSE b/packages/in_app_purchase/in_app_purchase_ohos/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..c6823b81eb845db89cee59cbbc7ee0b0b63d86ec --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/LICENSE @@ -0,0 +1,25 @@ +Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/in_app_purchase/in_app_purchase_ohos/README.md b/packages/in_app_purchase/in_app_purchase_ohos/README.md new file mode 100644 index 0000000000000000000000000000000000000000..971c114cbbe8c3e920073cd76066f81ef545796a --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/README.md @@ -0,0 +1,24 @@ +# in\_app\_purchase\_ohos + +The Ohos implementation of [`in_app_purchase`][1]. + +## Usage + +This package is [endorsed][2], which means you can simply use `in_app_purchase` +normally. This package will be automatically included in your app when you do, +so you do not need to add it to your `pubspec.yaml`. + +## Contributing + +This plugin utilizes +[json_serializable](https://pub.dev/packages/json_serializable) for the +numerous data structures communicated between the underlying platform layers and Dart. After +modifying any of the serialized data structures, regenerate the serializers by executing +`flutter pub run build_runner build --delete-conflicting-outputs`. +`flutter pub run build_runner watch --delete-conflicting-outputs` will supervise the filesystem for changes. + +If you are interested in contributing to the plugin, please consult our +[contribution guidelines](https://github.com/flutter/plugins/blob/main/CONTRIBUTING.md). + +[1]: https://pub.dev/packages/in_app_purchase +[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin diff --git a/packages/in_app_purchase/in_app_purchase_ohos/build.yaml b/packages/in_app_purchase/in_app_purchase_ohos/build.yaml new file mode 100644 index 0000000000000000000000000000000000000000..651a557fc1ca5de4484891911e24fd8c0e7addf4 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/build.yaml @@ -0,0 +1,8 @@ +# See https://pub.dev/packages/build_config +targets: + $default: + builders: + json_serializable: + options: + any_map: true + create_to_json: false diff --git a/packages/in_app_purchase/in_app_purchase_ohos/example/README.md b/packages/in_app_purchase/in_app_purchase_ohos/example/README.md new file mode 100644 index 0000000000000000000000000000000000000000..96b8bb17dbff8e7acd9a1dd59c65ff40b96e1228 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/example/README.md @@ -0,0 +1,9 @@ +# Platform Implementation Test App + +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/in_app_purchase/in_app_purchase_ohos/example/integration_test/in_app_purchase_test.dart b/packages/in_app_purchase/in_app_purchase_ohos/example/integration_test/in_app_purchase_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..28f99f748b4d199067672c3ffa2be0a32fcf979c --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/example/integration_test/in_app_purchase_test.dart @@ -0,0 +1,20 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:in_app_purchase_ohos/in_app_purchase_ohos.dart'; +import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart'; +import 'package:integration_test/integration_test.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('Can create InAppPurchaseStoreKit instance', + (WidgetTester tester) async { + InAppPurchaseOhosPlatform.registerPlatform(); + final InAppPurchasePlatform androidPlatform = + InAppPurchasePlatform.instance; + expect(androidPlatform, isNotNull); + }); +} diff --git a/packages/in_app_purchase/in_app_purchase_ohos/example/lib/consumable_store.dart b/packages/in_app_purchase/in_app_purchase_ohos/example/lib/consumable_store.dart new file mode 100644 index 0000000000000000000000000000000000000000..f8791d3b18c0524be8ec89cf02705cea880dd904 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/example/lib/consumable_store.dart @@ -0,0 +1,52 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'package:shared_preferences/shared_preferences.dart'; + +// ignore: avoid_classes_with_only_static_members +/// A store of consumable items. +/// +/// This is a development prototype that stores consumables in the shared +/// preferences. Do not use this in real world apps. +class ConsumableStore { + static const String _kPrefKey = 'consumables'; + static Future _writes = Future.value(); + + /// Adds a consumable with ID `id` to the store. + /// + /// The consumable is only added after the returned Future is complete. + static Future save(String id) { + _writes = _writes.then((void _) => _doSave(id)); + return _writes; + } + + /// Consumes a consumable with ID `id` from the store. + /// + /// The consumable was only consumed after the returned Future is complete. + static Future consume(String id) { + _writes = _writes.then((void _) => _doConsume(id)); + return _writes; + } + + /// Returns the list of consumables from the store. + static Future> load() async { + return (await SharedPreferences.getInstance()).getStringList(_kPrefKey) ?? + []; + } + + static Future _doSave(String id) async { + final List cached = await load(); + final SharedPreferences prefs = await SharedPreferences.getInstance(); + cached.add(id); + await prefs.setStringList(_kPrefKey, cached); + } + + static Future _doConsume(String id) async { + final List cached = await load(); + final SharedPreferences prefs = await SharedPreferences.getInstance(); + cached.remove(id); + await prefs.setStringList(_kPrefKey, cached); + } +} diff --git a/packages/in_app_purchase/in_app_purchase_ohos/example/lib/main.dart b/packages/in_app_purchase/in_app_purchase_ohos/example/lib/main.dart new file mode 100644 index 0000000000000000000000000000000000000000..d79a39189da8652f6e066a3efba0958f63cae015 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/example/lib/main.dart @@ -0,0 +1,414 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:in_app_purchase_ohos/in_app_purchase_ohos.dart'; +import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart'; + +import 'consumable_store.dart'; + +void main() { + WidgetsFlutterBinding.ensureInitialized(); + + // When using the Ohos plugin directly it is mandatory to register + // the plugin as default instance as part of initializing the app. + InAppPurchaseOhosPlatform.registerPlatform(); + + runApp(_MyApp()); +} + +const String _kConsumableId = 'consumable'; +const String _kUpgradeId = 'upgrade'; +const String _kSilverSubscriptionId = 'subscription_silver'; +const String _kGoldSubscriptionId = 'subscription_gold'; +const List _kProductIds = [ + _kConsumableId, + _kUpgradeId, + _kSilverSubscriptionId, + _kGoldSubscriptionId, +]; + +class _MyApp extends StatefulWidget { + @override + State<_MyApp> createState() => _MyAppState(); +} + +class _MyAppState extends State<_MyApp> { + final InAppPurchaseOhosPlatform _iapStoreKitPlatform = + InAppPurchasePlatform.instance as InAppPurchaseOhosPlatform; + late StreamSubscription> _subscription; + List _notFoundIds = []; + List _products = []; + List _purchases = []; + List _consumables = []; + bool _isAvailable = false; + bool _purchasePending = false; + bool _loading = true; + String? _queryProductError; + + @override + void initState() { + final Stream> purchaseUpdated = + _iapStoreKitPlatform.purchaseStream; + _subscription = + purchaseUpdated.listen((List purchaseDetailsList) { + _listenToPurchaseUpdated(purchaseDetailsList); + }, onDone: () { + _subscription.cancel(); + }, onError: (Object error) { + // handle error here. + }); + + initStoreInfo(); + super.initState(); + } + + Future initStoreInfo() async { + final bool isAvailable = await _iapStoreKitPlatform.isAvailable(); + if (!isAvailable) { + setState(() { + _isAvailable = isAvailable; + _products = []; + _purchases = []; + _notFoundIds = []; + _consumables = []; + _purchasePending = false; + _loading = false; + }); + return; + } + final ProductDetailsResponse productDetailResponse = + await _iapStoreKitPlatform.queryProductDetails(_kProductIds.toSet()); + if (productDetailResponse.error != null) { + setState(() { + _queryProductError = productDetailResponse.error!.message; + _isAvailable = isAvailable; + _products = productDetailResponse.productDetails; + _purchases = []; + _notFoundIds = productDetailResponse.notFoundIDs; + _consumables = []; + _purchasePending = false; + _loading = false; + }); + return; + } + + if (productDetailResponse.productDetails.isEmpty) { + setState(() { + _queryProductError = null; + _isAvailable = isAvailable; + _products = productDetailResponse.productDetails; + _purchases = []; + _notFoundIds = productDetailResponse.notFoundIDs; + _consumables = []; + _purchasePending = false; + _loading = false; + }); + return; + } + + final List consumables = await ConsumableStore.load(); + setState(() { + _isAvailable = isAvailable; + _products = productDetailResponse.productDetails; + _notFoundIds = productDetailResponse.notFoundIDs; + _consumables = consumables; + _purchasePending = false; + _loading = false; + }); + } + + @override + void dispose() { + _subscription.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final List stack = []; + if (_queryProductError == null) { + stack.add( + ListView( + children: [ + _buildConnectionCheckTile(), + _buildProductList(), + _buildConsumableBox(), + // _buildRestoreButton(), + ], + ), + ); + } else { + stack.add(Center( + child: Text(_queryProductError!), + )); + } + if (_purchasePending) { + stack.add( + // const Stack( + Stack( + children: [ + Opacity( + opacity: 0.3, + child: ModalBarrier(dismissible: false, color: Colors.grey), + ), + Center( + child: CircularProgressIndicator(), + ), + ], + ), + ); + } + + return MaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text('IAP Example'), + ), + body: Stack( + children: stack, + ), + ), + ); + } + + Card _buildConnectionCheckTile() { + if (_loading) { + return const Card(child: ListTile(title: Text('Trying to connect...'))); + } + final Widget storeHeader = ListTile( + leading: Icon(_isAvailable ? Icons.check : Icons.block, + color: _isAvailable + ? Colors.green + : ThemeData.light().colorScheme.error), + title: + Text('The store is ${_isAvailable ? 'available' : 'unavailable'}.'), + ); + final List children = [storeHeader]; + + if (!_isAvailable) { + children.addAll([ + const Divider(), + ListTile( + title: Text('Not connected', + style: TextStyle(color: ThemeData.light().colorScheme.error)), + subtitle: const Text( + 'Unable to connect to the payments processor. Has this app been configured correctly? See the example README for instructions.'), + ), + ]); + } + return Card(child: Column(children: children)); + } + + Card _buildProductList() { + if (_loading) { + return const Card( + child: ListTile( + leading: CircularProgressIndicator(), + title: Text('Fetching products...'))); + } + if (!_isAvailable) { + return const Card(); + } + const ListTile productHeader = ListTile(title: Text('Products for Sale')); + final List productList = []; + if (_notFoundIds.isNotEmpty) { + productList.add(ListTile( + title: Text('[${_notFoundIds.join(", ")}] not found', + style: TextStyle(color: ThemeData.light().colorScheme.error)), + subtitle: const Text( + 'This app needs special configuration to run. Please see example/README.md for instructions.'))); + } + + // This loading previous purchases code is just a demo. Please do not use this as it is. + // In your app you should always verify the purchase data using the `verificationData` inside the [PurchaseDetails] object before trusting it. + // We recommend that you use your own server to verify the purchase data. + final Map purchases = + Map.fromEntries( + _purchases.map((PurchaseDetails purchase) { + if (purchase.pendingCompletePurchase) { + _iapStoreKitPlatform.completePurchase(purchase); + } + return MapEntry(purchase.productID, purchase); + })); + productList.addAll(_products.map( + (ProductDetails productDetails) { + final PurchaseDetails? previousPurchase = purchases[productDetails.id]; + return ListTile( + title: Text( + productDetails.title, + ), + subtitle: Text( + productDetails.description, + ), + trailing: previousPurchase != null + ? SizedBox.shrink() + : TextButton( + style: TextButton.styleFrom( + backgroundColor: Colors.green[800], + foregroundColor: Colors.white, + ), + onPressed: () { + final PurchaseParam purchaseParam = PurchaseParam( + productDetails: productDetails, + ); + if (productDetails.id == _kConsumableId) { + _iapStoreKitPlatform.buyConsumable( + purchaseParam: purchaseParam); + } else { + _iapStoreKitPlatform.buyNonConsumable( + purchaseParam: purchaseParam); + } + }, + child: Text(productDetails.price), + )); + }, + )); + + return Card( + child: Column( + children: [productHeader, const Divider()] + productList)); + } + + Card _buildConsumableBox() { + if (_loading) { + return const Card( + child: ListTile( + leading: CircularProgressIndicator(), + title: Text('Fetching consumables...'))); + } + if (!_isAvailable || _notFoundIds.contains(_kConsumableId)) { + return const Card(); + } + const ListTile consumableHeader = + ListTile(title: Text('Purchased consumables')); + final List tokens = _consumables.map((String id) { + return GridTile( + child: IconButton( + icon: const Icon( + Icons.stars, + size: 42.0, + color: Colors.orange, + ), + splashColor: Colors.yellowAccent, + onPressed: () => consume(id), + ), + ); + }).toList(); + return Card( + child: Column(children: [ + consumableHeader, + const Divider(), + GridView.count( + crossAxisCount: 5, + shrinkWrap: true, + padding: const EdgeInsets.all(16.0), + children: tokens, + ) + ])); + } + + Widget _buildRestoreButton() { + if (_loading) { + return Container(); + } + + return Padding( + padding: const EdgeInsets.all(4.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton( + style: TextButton.styleFrom( + backgroundColor: Theme.of(context).colorScheme.primary, + foregroundColor: Colors.white, + ), + onPressed: () => _iapStoreKitPlatform.restorePurchases(), + child: const Text('Restore purchases'), + ), + ], + ), + ); + } + + Future consume(String id) async { + await ConsumableStore.consume(id); + final List consumables = await ConsumableStore.load(); + setState(() { + _consumables = consumables; + }); + } + + void showPendingUI() { + setState(() { + _purchasePending = true; + }); + } + + Future deliverProduct(PurchaseDetails purchaseDetails) async { + // IMPORTANT!! Always verify purchase details before delivering the product. + if (purchaseDetails.productID == _kConsumableId) { + await ConsumableStore.save(purchaseDetails.purchaseID!); + final List consumables = await ConsumableStore.load(); + setState(() { + _purchasePending = false; + _consumables = consumables; + }); + } else { + setState(() { + _purchases.add(purchaseDetails); + _purchasePending = false; + }); + } + } + + void handleError(IAPError error) { + setState(() { + _purchasePending = false; + }); + } + + Future _verifyPurchase(PurchaseDetails purchaseDetails) { + // IMPORTANT!! Always verify a purchase before delivering the product. + // For the purpose of an example, we directly return true. + return Future.value(true); + } + + void _handleInvalidPurchase(PurchaseDetails purchaseDetails) { + // handle invalid purchase here if _verifyPurchase` failed. + } + + void _listenToPurchaseUpdated(List purchaseDetailsList) { + purchaseDetailsList.forEach(_handleReportedPurchaseState); + } + + Future _handleReportedPurchaseState( + PurchaseDetails purchaseDetails) async { + print('InAppPurchasePlugin purchaseDetails.status =$purchaseDetails'); + if (purchaseDetails.status == PurchaseStatus.pending) { + showPendingUI(); + } else { + if (purchaseDetails.status == PurchaseStatus.error) { + handleError(purchaseDetails.error!); + } else if (purchaseDetails.status == PurchaseStatus.purchased || + purchaseDetails.status == PurchaseStatus.restored) { + final bool valid = await _verifyPurchase(purchaseDetails); + if (valid) { + await deliverProduct(purchaseDetails); + } else { + _handleInvalidPurchase(purchaseDetails); + return; + } + }else if(purchaseDetails.status == PurchaseStatus.canceled){ + + } + + if (purchaseDetails.pendingCompletePurchase) { + await _iapStoreKitPlatform.completePurchase(purchaseDetails); + } + } + } +} diff --git a/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/.gitignore b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..6ca13b3170eec5dd5ac5ad7f1c4dd0118845f473 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/.gitignore @@ -0,0 +1,19 @@ +/node_modules +/oh_modules +/local.properties +/.idea +**/build +/.hvigor +.cxx +/.clangd +/.clang-format +/.clang-tidy +**/.test +*.har +**/BuildProfile.ets +**/oh-package-lock.json5 + +**/src/main/resources/rawfile/flutter_assets/ +**/libs/arm64-v8a/libapp.so +**/libs/arm64-v8a/libflutter.so +**/libs/arm64-v8a/libvmservice_snapshot.so diff --git a/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/AppScope/app.json5 b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/AppScope/app.json5 new file mode 100644 index 0000000000000000000000000000000000000000..a13c8bfdc16a34697a1ce5c7dc8e207275149473 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/AppScope/app.json5 @@ -0,0 +1,10 @@ +{ + "app": { + "bundleName": "com.example.in_app_purchase_ohos", + "vendor": "example", + "versionCode": 1000000, + "versionName": "1.0.0", + "icon": "$media:app_icon", + "label": "$string:app_name" + } +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/AppScope/resources/base/element/string.json b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/AppScope/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..810f4a362c1d177309eec4f2efe5cac2f4558c28 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "example" + } + ] +} diff --git a/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/AppScope/resources/base/media/app_icon.png b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/AppScope/resources/base/media/app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c Binary files /dev/null and b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/AppScope/resources/base/media/app_icon.png differ diff --git a/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/build-profile.json5 b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..1d12140d202702d7c73d64f1b291fe5c45a660ce --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/build-profile.json5 @@ -0,0 +1,27 @@ +{ + "app": { + "signingConfigs": [], + "products": [ + { + "name": "default", + "signingConfig": "default", + "compatibleSdkVersion": "5.0.0(12)", + "runtimeOS": "HarmonyOS" + } + ] + }, + "modules": [ + { + "name": "entry", + "srcPath": "./entry", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/.gitignore b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..2795a1c5b1fe53659dd1b71d90ba0592eaf7e043 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/.gitignore @@ -0,0 +1,7 @@ + +/node_modules +/oh_modules +/.preview +/build +/.cxx +/.test \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/build-profile.json5 b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..633d360fbc91a3186a23b66ab71b27e5618944cb --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/build-profile.json5 @@ -0,0 +1,29 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +{ + "apiType": 'stageMode', + "buildOption": { + }, + "targets": [ + { + "name": "default", + "runtimeOS": "HarmonyOS" + }, + { + "name": "ohosTest", + } + ] +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/hvigorfile.ts b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..5bda56eeac3f79703639db986e2faaa433b0e48c --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/hvigorfile.ts @@ -0,0 +1,17 @@ +/* +* Copyright (c) 2024 SwanLink (Jiangsu) Technology Development Co., LTD. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently. +export { hapTasks } from '@ohos/hvigor-ohos-plugin'; diff --git a/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/oh-package.json5 b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..0dabd8f8158dd0c7ef1dd55fc8361f234192f9d0 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/oh-package.json5 @@ -0,0 +1,13 @@ +{ + "name": "entry", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": { + "in_app_purchase_ohos": "file:../har/in_app_purchase_ohos.har", + "integration_test": "file:../har/integration_test.har", + "shared_preferences_ohos": "file:../har/shared_preferences_ohos.har" + } +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/main/ets/entryability/EntryAbility.ets b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/main/ets/entryability/EntryAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..b5888ed9002dd328f9abd3141ee3e63e88b40d0f --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/main/ets/entryability/EntryAbility.ets @@ -0,0 +1,24 @@ +/* +* Copyright (c) 2024 SwanLink (Jiangsu) Technology Development Co., LTD. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import { FlutterAbility, FlutterEngine } from '@ohos/flutter_ohos'; +import { GeneratedPluginRegistrant } from '../plugins/GeneratedPluginRegistrant'; + +export default class EntryAbility extends FlutterAbility { + configureFlutterEngine(flutterEngine: FlutterEngine) { + super.configureFlutterEngine(flutterEngine) + GeneratedPluginRegistrant.registerWith(flutterEngine) + } +} diff --git a/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/main/ets/pages/Index.ets b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/main/ets/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..5d9647743f4651f3dab7aa3076ed0707b207b436 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/main/ets/pages/Index.ets @@ -0,0 +1,38 @@ +/* +* Copyright (c) 2024 SwanLink (Jiangsu) Technology Development Co., LTD. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import common from '@ohos.app.ability.common'; +import { FlutterPage } from '@ohos/flutter_ohos' + +let storage = LocalStorage.getShared() +const EVENT_BACK_PRESS = 'EVENT_BACK_PRESS' + +@Entry(storage) +@Component +struct Index { + private context = getContext(this) as common.UIAbilityContext + @LocalStorageLink('viewId') viewId: string = ""; + + build() { + Column() { + FlutterPage({ viewId: this.viewId }) + } + } + + onBackPress(): boolean { + this.context.eventHub.emit(EVENT_BACK_PRESS) + return true + } +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/main/ets/plugins/GeneratedPluginRegistrant.ets b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/main/ets/plugins/GeneratedPluginRegistrant.ets new file mode 100644 index 0000000000000000000000000000000000000000..0569b3bbcb6759d2988addd26cbefff06fd96c4a --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/main/ets/plugins/GeneratedPluginRegistrant.ets @@ -0,0 +1,45 @@ +/* +* Copyright (c) 2024 SwanLink (Jiangsu) Technology Development Co., LTD. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import { FlutterEngine, Log } from '@ohos/flutter_ohos'; +import InAppPurchasePlugin from 'in_app_purchase_ohos'; +import IntegrationTestPlugin from 'integration_test'; +import SharedPreferencesPlugin from 'shared_preferences_ohos'; + +/** + * Generated file. Do not edit. + * This file is generated by the Flutter tool based on the + * plugins that support the Ohos platform. + */ + +const TAG = "GeneratedPluginRegistrant"; + +export class GeneratedPluginRegistrant { + + static registerWith(flutterEngine: FlutterEngine) { + try { + flutterEngine.getPlugins()?.add(new InAppPurchasePlugin()); + flutterEngine.getPlugins()?.add(new IntegrationTestPlugin()); + flutterEngine.getPlugins()?.add(new SharedPreferencesPlugin()); + } catch (e) { + Log.e( + TAG, + "Tried to register plugins with FlutterEngine (" + + flutterEngine + + ") failed."); + Log.e(TAG, "Received exception while registering", e); + } + } +} diff --git a/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/main/module.json5 b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..7bbf78b18f39991b1404061c7437538c7d532bb7 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/main/module.json5 @@ -0,0 +1,53 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +{ + "module": { + "name": "entry", + "type": "entry", + "description": "$string:module_desc", + "mainElement": "EntryAbility", + "deviceTypes": [ + "phone" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "abilities": [ + { + "name": "EntryAbility", + "srcEntry": "./ets/entryability/EntryAbility.ets", + "description": "$string:EntryAbility_desc", + "icon": "$media:icon", + "label": "$string:EntryAbility_label", + "startWindowIcon": "$media:icon", + "startWindowBackground": "$color:start_window_background", + "exported": true, + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ] + } + ], + "requestPermissions": [ + {"name" : "ohos.permission.INTERNET"}, + ] + } +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/main/resources/base/element/color.json b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..3c712962da3c2751c2b9ddb53559afcbd2b54a02 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/main/resources/base/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#FFFFFF" + } + ] +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/main/resources/base/element/string.json b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..67e0f4ff4ac762d1714f6e215c6636a4ad3d620e --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/main/resources/base/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "example" + } + ] +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/main/resources/base/media/icon.png b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/main/resources/base/media/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c Binary files /dev/null and b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/main/resources/base/media/icon.png differ diff --git a/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/main/resources/base/profile/main_pages.json b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..1898d94f58d6128ab712be2c68acc7c98e9ab9ce --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "pages/Index" + ] +} diff --git a/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/main/resources/en_US/element/string.json b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/main/resources/en_US/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..67e0f4ff4ac762d1714f6e215c6636a4ad3d620e --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/main/resources/en_US/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "example" + } + ] +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/main/resources/zh_CN/element/string.json b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/main/resources/zh_CN/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..601e2b5a1c273aa04920b126e3ab715a4450e58f --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "模块描述" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "example" + } + ] +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/ohosTest/ets/test/Ability.test.ets b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/ohosTest/ets/test/Ability.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..bdb2d4b3479e7c4b3d4ffb539abc734470c57f32 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/ohosTest/ets/test/Ability.test.ets @@ -0,0 +1,50 @@ +/* +* Copyright (c) 2024 SwanLink (Jiangsu) Technology Development Co., LTD. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import hilog from '@ohos.hilog'; +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium' + +export default function abilityTest() { + describe('ActsAbilityTest', function () { + // Defines a test suite. Two parameters are supported: test suite name and test suite function. + beforeAll(function () { + // Presets an action, which is performed only once before all test cases of the test suite start. + // This API supports only one parameter: preset action function. + }) + beforeEach(function () { + // Presets an action, which is performed before each unit test case starts. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: preset action function. + }) + afterEach(function () { + // Presets a clear action, which is performed after each unit test case ends. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: clear action function. + }) + afterAll(function () { + // Presets a clear action, which is performed after all test cases of the test suite end. + // This API supports only one parameter: clear action function. + }) + it('assertContain',0, function () { + // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. + hilog.info(0x0000, 'testTag', '%{public}s', 'it begin'); + let a = 'abc' + let b = 'b' + // Defines a variety of assertion methods, which are used to declare expected boolean conditions. + expect(a).assertContain(b) + expect(a).assertEqual(a) + }) + }) +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/ohosTest/ets/test/List.test.ets b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/ohosTest/ets/test/List.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..5ed99383f79bb67de7472f356de5b0421fa26e71 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/ohosTest/ets/test/List.test.ets @@ -0,0 +1,20 @@ +/* +* Copyright (c) 2024 SwanLink (Jiangsu) Technology Development Co., LTD. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import abilityTest from './Ability.test' + +export default function testsuite() { + abilityTest() +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/ohosTest/ets/testability/TestAbility.ets b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/ohosTest/ets/testability/TestAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..465211ee2e66f8b16d8c28881acc0f534df700e5 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/ohosTest/ets/testability/TestAbility.ets @@ -0,0 +1,63 @@ +/* +* Copyright (c) 2024 SwanLink (Jiangsu) Technology Development Co., LTD. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import UIAbility from '@ohos.app.ability.UIAbility'; +import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry'; +import hilog from '@ohos.hilog'; +import { Hypium } from '@ohos/hypium'; +import testsuite from '../test/List.test'; +import window from '@ohos.window'; + +export default class TestAbility extends UIAbility { + onCreate(want, launchParam) { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onCreate'); + hilog.info(0x0000, 'testTag', '%{public}s', 'want param:' + JSON.stringify(want) ?? ''); + hilog.info(0x0000, 'testTag', '%{public}s', 'launchParam:'+ JSON.stringify(launchParam) ?? ''); + var abilityDelegator: any + abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator() + var abilityDelegatorArguments: any + abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments() + hilog.info(0x0000, 'testTag', '%{public}s', 'start run testcase!!!'); + Hypium.hypiumTest(abilityDelegator, abilityDelegatorArguments, testsuite) + } + + onDestroy() { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onDestroy'); + } + + onWindowStageCreate(windowStage: window.WindowStage) { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageCreate'); + windowStage.loadContent('testability/pages/Index', (err, data) => { + if (err.code) { + hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); + return; + } + hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', + JSON.stringify(data) ?? ''); + }); + } + + onWindowStageDestroy() { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageDestroy'); + } + + onForeground() { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onForeground'); + } + + onBackground() { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onBackground'); + } +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/ohosTest/ets/testability/pages/Index.ets b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/ohosTest/ets/testability/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..bdf1e5f905bcacd6a73af7709c41d4a79d41b170 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/ohosTest/ets/testability/pages/Index.ets @@ -0,0 +1,49 @@ +/* +* Copyright (c) 2024 SwanLink (Jiangsu) Technology Development Co., LTD. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import hilog from '@ohos.hilog'; + +@Entry +@Component +struct Index { + aboutToAppear() { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility index aboutToAppear'); + } + @State message: string = 'Hello World' + build() { + Row() { + Column() { + Text(this.message) + .fontSize(50) + .fontWeight(FontWeight.Bold) + Button() { + Text('next page') + .fontSize(20) + .fontWeight(FontWeight.Bold) + }.type(ButtonType.Capsule) + .margin({ + top: 20 + }) + .backgroundColor('#0D9FFB') + .width('35%') + .height('5%') + .onClick(()=>{ + }) + } + .width('100%') + } + .height('100%') + } + } \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts new file mode 100644 index 0000000000000000000000000000000000000000..58d9c312f08e7c9ac01e4d6f2d0a33ddc6188ed9 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts @@ -0,0 +1,64 @@ +/* +* Copyright (c) 2024 SwanLink (Jiangsu) Technology Development Co., LTD. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import hilog from '@ohos.hilog'; +import TestRunner from '@ohos.application.testRunner'; +import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry'; + +var abilityDelegator = undefined +var abilityDelegatorArguments = undefined + +async function onAbilityCreateCallback() { + hilog.info(0x0000, 'testTag', '%{public}s', 'onAbilityCreateCallback'); +} + +async function addAbilityMonitorCallback(err: any) { + hilog.info(0x0000, 'testTag', 'addAbilityMonitorCallback : %{public}s', JSON.stringify(err) ?? ''); +} + +export default class OpenHarmonyTestRunner implements TestRunner { + constructor() { + } + + onPrepare() { + hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner OnPrepare '); + } + + async onRun() { + hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun run'); + abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments() + abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator() + var testAbilityName = abilityDelegatorArguments.bundleName + '.TestAbility' + let lMonitor = { + abilityName: testAbilityName, + onAbilityCreate: onAbilityCreateCallback, + }; + abilityDelegator.addAbilityMonitor(lMonitor, addAbilityMonitorCallback) + var cmd = 'aa start -d 0 -a TestAbility' + ' -b ' + abilityDelegatorArguments.bundleName + var debug = abilityDelegatorArguments.parameters['-D'] + if (debug == 'true') + { + cmd += ' -D' + } + hilog.info(0x0000, 'testTag', 'cmd : %{public}s', cmd); + abilityDelegator.executeShellCommand(cmd, + (err: any, d: any) => { + hilog.info(0x0000, 'testTag', 'executeShellCommand : err : %{public}s', JSON.stringify(err) ?? ''); + hilog.info(0x0000, 'testTag', 'executeShellCommand : data : %{public}s', d.stdResult ?? ''); + hilog.info(0x0000, 'testTag', 'executeShellCommand : data : %{public}s', d.exitCode ?? ''); + }) + hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun end'); + } +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/ohosTest/module.json5 b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/ohosTest/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..fab77ce2e0c61e3ad010bab5b27ccbd15f9a8c96 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/ohosTest/module.json5 @@ -0,0 +1,51 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +{ + "module": { + "name": "entry_test", + "type": "feature", + "description": "$string:module_test_desc", + "mainElement": "TestAbility", + "deviceTypes": [ + "phone" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:test_pages", + "abilities": [ + { + "name": "TestAbility", + "srcEntry": "./ets/testability/TestAbility.ets", + "description": "$string:TestAbility_desc", + "icon": "$media:icon", + "label": "$string:TestAbility_label", + "exported": true, + "startWindowIcon": "$media:icon", + "startWindowBackground": "$color:start_window_background", + "skills": [ + { + "actions": [ + "action.system.home" + ], + "entities": [ + "entity.system.home" + ] + } + ] + } + ] + } +} diff --git a/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/ohosTest/resources/base/element/color.json b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/ohosTest/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..3c712962da3c2751c2b9ddb53559afcbd2b54a02 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/ohosTest/resources/base/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#FFFFFF" + } + ] +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/ohosTest/resources/base/element/string.json b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/ohosTest/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..65d8fa5a7cf54aa3943dcd0214f58d1771bc1f6c --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/ohosTest/resources/base/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_test_desc", + "value": "test ability description" + }, + { + "name": "TestAbility_desc", + "value": "the test ability" + }, + { + "name": "TestAbility_label", + "value": "test label" + } + ] +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/ohosTest/resources/base/media/icon.png b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/ohosTest/resources/base/media/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c Binary files /dev/null and b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/ohosTest/resources/base/media/icon.png differ diff --git a/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/ohosTest/resources/base/profile/test_pages.json b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/ohosTest/resources/base/profile/test_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..b7e7343cacb32ce982a45e76daad86e435e054fe --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/entry/src/ohosTest/resources/base/profile/test_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "testability/pages/Index" + ] +} diff --git a/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/hvigor/hvigor-config.json5 b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/hvigor/hvigor-config.json5 new file mode 100644 index 0000000000000000000000000000000000000000..541ba35711b75986f9295410ee38fdb8f2572878 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/hvigor/hvigor-config.json5 @@ -0,0 +1,20 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +{ + "modelVersion": "5.0.0", + "dependencies": { + } +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/hvigorfile.ts b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..38626e385a5b47dd3cba0e1e83c614f091b7cc9e --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/hvigorfile.ts @@ -0,0 +1,21 @@ +/* +* Copyright (c) 2024 SwanLink (Jiangsu) Technology Development Co., LTD. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import { appTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/oh-package.json5 b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..81da8c679151fb90deafcfea3617d7463ac9a0d1 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/example/ohos/oh-package.json5 @@ -0,0 +1,22 @@ +{ + "modelVersion": "5.0.0", + "name": "example", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": { + "@ohos/flutter_ohos": "file:./har/flutter.har" + }, + "devDependencies": { + "@ohos/hypium": "1.0.6" + }, + "overrides": { + "@ohos/flutter_ohos": "file:./har/flutter.har", + "in_app_purchase_ohos": "file:./har/in_app_purchase_ohos.har", + "shared_preferences_ohos": "file:./har/shared_preferences_ohos.har", + "integration_test": "file:./har/integration_test.har", + "@ohos/flutter_module": "file:./entry" + } +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_ohos/example/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_ohos/example/pubspec.yaml new file mode 100644 index 0000000000000000000000000000000000000000..657c9d1cab93a692ef1e97588d82bcee34eb5bc6 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/example/pubspec.yaml @@ -0,0 +1,33 @@ +name: in_app_purchase_ohos_example +description: Demonstrates how to use the in_app_purchase_ohos plugin. +publish_to: none + +environment: + sdk: ">=2.19.0 <4.0.0" + flutter: ">=3.7.0" + +dependencies: + flutter: + sdk: flutter + in_app_purchase_ohos: + # When depending on this package from a real application you should use: + # in_app_purchase_ohos: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + in_app_purchase_platform_interface: ^1.0.0 + shared_preferences: + git: + url: https://gitee.com/openharmony-sig/flutter_packages.git + path: packages/shared_preferences/shared_preferences + +dev_dependencies: + # build_runner: ^2.4.5 + flutter_test: + sdk: flutter + integration_test: + sdk: flutter + +flutter: + uses-material-design: true diff --git a/packages/in_app_purchase/in_app_purchase_ohos/lib/iap_kit_wrappers.dart b/packages/in_app_purchase/in_app_purchase_ohos/lib/iap_kit_wrappers.dart new file mode 100644 index 0000000000000000000000000000000000000000..eade8d181a423271bdd006693187b3bceb3e3266 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/lib/iap_kit_wrappers.dart @@ -0,0 +1,9 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +export 'src/iap_kit_wrappers/ik_payment_queue_wrapper.dart'; +export 'src/iap_kit_wrappers/ik_payment_transaction_wrappers.dart'; +export 'src/iap_kit_wrappers/ik_product_wrapper.dart'; +export 'src/iap_kit_wrappers/ik_receipt_manager.dart'; +export 'src/iap_kit_wrappers/ik_request_maker.dart'; diff --git a/packages/in_app_purchase/in_app_purchase_ohos/lib/in_app_purchase_ohos.dart b/packages/in_app_purchase/in_app_purchase_ohos/lib/in_app_purchase_ohos.dart new file mode 100644 index 0000000000000000000000000000000000000000..bee9e1c9af5a87b59b3b834cd115de3db5cbff3e --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/lib/in_app_purchase_ohos.dart @@ -0,0 +1,7 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +export 'src/in_app_purchase_ohos_platform.dart'; +export 'src/in_app_purchase_ohos_platform_addition.dart'; +export 'src/types/types.dart'; diff --git a/packages/in_app_purchase/in_app_purchase_ohos/lib/src/channel.dart b/packages/in_app_purchase/in_app_purchase_ohos/lib/src/channel.dart new file mode 100644 index 0000000000000000000000000000000000000000..c8b9d647a28c30b58e641ab229c21982c8f789e1 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/lib/src/channel.dart @@ -0,0 +1,9 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/services.dart'; + +/// Method channel for the plugin's platform<-->Dart calls. +const MethodChannel channel = + MethodChannel('plugins.flutter.io/in_app_purchase'); \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_ohos/lib/src/iap_kit_wrappers/README.md b/packages/in_app_purchase/in_app_purchase_ohos/lib/src/iap_kit_wrappers/README.md new file mode 100644 index 0000000000000000000000000000000000000000..8a99aab65991bcdb9538d628393657dc0c65bc62 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/lib/src/iap_kit_wrappers/README.md @@ -0,0 +1,5 @@ +# iap_kit_wrappers + +This exposes Dart endpoints through to the +IAP Kit APIs. It offers functionality +as an alternative to [in_app_purchase](../in_app_purchase/README.md). \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_ohos/lib/src/iap_kit_wrappers/enum_converters.dart b/packages/in_app_purchase/in_app_purchase_ohos/lib/src/iap_kit_wrappers/enum_converters.dart new file mode 100644 index 0000000000000000000000000000000000000000..76e1932354e1e339d5d250164c32b481ce949ded --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/lib/src/iap_kit_wrappers/enum_converters.dart @@ -0,0 +1,143 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart'; +import 'package:json_annotation/json_annotation.dart'; + +import '../../iap_kit_wrappers.dart'; + +part 'enum_converters.g.dart'; + +/// Serializer for [IKPaymentTransactionStateWrapper]. +/// +/// Use these in `@JsonSerializable()` classes by annotating them with +/// `@IKTransactionStatusConverter()`. +class IKTransactionStatusConverter + implements JsonConverter { + /// Default const constructor. + const IKTransactionStatusConverter(); + + @override + IKPaymentTransactionStateWrapper fromJson(int? json) { + if (json == null) { + return IKPaymentTransactionStateWrapper.unspecified; + } + return $enumDecode( + _$IKPaymentTransactionStateWrapperEnumMap + .cast(), + json); + } + + /// Converts an [IKPaymentTransactionStateWrapper] to a [PurchaseStatus]. + PurchaseStatus toPurchaseStatus( + IKPaymentTransactionStateWrapper object, IKError? error) { + print('MethodCallHandlerImpl toPurchaseStatus. $object'); + switch (object) { + case IKPaymentTransactionStateWrapper.purchasing: + case IKPaymentTransactionStateWrapper.deferred: + return PurchaseStatus.pending; + case IKPaymentTransactionStateWrapper.purchased: + return PurchaseStatus.purchased; + case IKPaymentTransactionStateWrapper.restored: + return PurchaseStatus.restored; + case IKPaymentTransactionStateWrapper.failed: + // According to the Apple documentation the error code "2" indicates + // the user cancelled the payment (IKErrorPaymentCancelled) and error + // code "15" indicates the cancellation of the overlay (IKErrorOverlayCancelled). + if (error != null && (error.code == 2 || error.code == 15)) { + return PurchaseStatus.canceled; + } + return PurchaseStatus.error; + case IKPaymentTransactionStateWrapper.unspecified: + return PurchaseStatus.error; + } + } + + @override + int toJson(IKPaymentTransactionStateWrapper object) => + _$IKPaymentTransactionStateWrapperEnumMap[object]!; +} + +/// Serializer for [IKSubscriptionPeriodUnit]. +/// +/// Use these in `@JsonSerializable()` classes by annotating them with +/// `@IKSubscriptionPeriodUnitConverter()`. +class IKSubscriptionPeriodUnitConverter + implements JsonConverter { + /// Default const constructor. + const IKSubscriptionPeriodUnitConverter(); + + @override + IKSubscriptionPeriodUnit fromJson(int? json) { + if (json == null) { + return IKSubscriptionPeriodUnit.day; + } + return $enumDecode( + _$IKSubscriptionPeriodUnitEnumMap + .cast(), + json); + } + + @override + int toJson(IKSubscriptionPeriodUnit object) => + _$IKSubscriptionPeriodUnitEnumMap[object]!; +} + +/// Serializer for [IKProductDiscountPaymentMode]. +/// +/// Use these in `@JsonSerializable()` classes by annotating them with +/// `@IKProductDiscountPaymentModeConverter()`. +class IKProductDiscountPaymentModeConverter + implements JsonConverter { + /// Default const constructor. + const IKProductDiscountPaymentModeConverter(); + + @override + IKProductDiscountPaymentMode fromJson(int? json) { + if (json == null) { + return IKProductDiscountPaymentMode.payAsYouGo; + } + return $enumDecode( + _$IKProductDiscountPaymentModeEnumMap + .cast(), + json); + } + + @override + int toJson(IKProductDiscountPaymentMode object) => + _$IKProductDiscountPaymentModeEnumMap[object]!; +} + +// Define a class so we generate serializer helper methods for the enums +// See https://github.com/google/json_serializable.dart/issues/778 +@JsonSerializable() +class _SerializedEnums { + late IKPaymentTransactionStateWrapper response; + late IKSubscriptionPeriodUnit unit; + late IKProductDiscountPaymentMode discountPaymentMode; +} + +/// Serializer for [IKProductDiscountType]. +/// +/// Use these in `@JsonSerializable()` classes by annotating them with +/// `@IKProductDiscountTypeConverter()`. +class IKProductDiscountTypeConverter + implements JsonConverter { + /// Default const constructor. + const IKProductDiscountTypeConverter(); + + @override + IKProductDiscountType fromJson(int? json) { + if (json == null) { + return IKProductDiscountType.introductory; + } + return $enumDecode( + _$IKProductDiscountTypeEnumMap.cast(), + json); + } + + @override + int toJson(IKProductDiscountType object) => + _$IKProductDiscountTypeEnumMap[object]!; +} diff --git a/packages/in_app_purchase/in_app_purchase_ohos/lib/src/iap_kit_wrappers/enum_converters.g.dart b/packages/in_app_purchase/in_app_purchase_ohos/lib/src/iap_kit_wrappers/enum_converters.g.dart new file mode 100644 index 0000000000000000000000000000000000000000..1fd872ac6fc51499b191a0a4e1868f47569c55af --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/lib/src/iap_kit_wrappers/enum_converters.g.dart @@ -0,0 +1,42 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'enum_converters.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_SerializedEnums _$SerializedEnumsFromJson(Map json) => _SerializedEnums() + ..response = + $enumDecode(_$IKPaymentTransactionStateWrapperEnumMap, json['response']) + ..unit = $enumDecode(_$IKSubscriptionPeriodUnitEnumMap, json['unit']) + ..discountPaymentMode = $enumDecode( + _$IKProductDiscountPaymentModeEnumMap, json['discountPaymentMode']); + +const _$IKPaymentTransactionStateWrapperEnumMap = { + IKPaymentTransactionStateWrapper.purchasing: 0, + IKPaymentTransactionStateWrapper.purchased: 1, + IKPaymentTransactionStateWrapper.failed: 2, + IKPaymentTransactionStateWrapper.restored: 3, + IKPaymentTransactionStateWrapper.deferred: 4, + IKPaymentTransactionStateWrapper.unspecified: -1, +}; + +const _$IKSubscriptionPeriodUnitEnumMap = { + IKSubscriptionPeriodUnit.day: 0, + IKSubscriptionPeriodUnit.week: 1, + IKSubscriptionPeriodUnit.month: 2, + IKSubscriptionPeriodUnit.year: 3, +}; + +const _$IKProductDiscountPaymentModeEnumMap = { + IKProductDiscountPaymentMode.payAsYouGo: 0, + IKProductDiscountPaymentMode.payUpFront: 1, + IKProductDiscountPaymentMode.freeTrail: 2, + IKProductDiscountPaymentMode.unspecified: -1, +}; + +const _$IKProductDiscountTypeEnumMap = { + IKProductDiscountType.introductory: 0, + IKProductDiscountType.subscription: 1, +}; diff --git a/packages/in_app_purchase/in_app_purchase_ohos/lib/src/iap_kit_wrappers/ik_payment_queue_wrapper.dart b/packages/in_app_purchase/in_app_purchase_ohos/lib/src/iap_kit_wrappers/ik_payment_queue_wrapper.dart new file mode 100644 index 0000000000000000000000000000000000000000..c4fdc94af11cc5d47573d11eabb84c4236ff0b41 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/lib/src/iap_kit_wrappers/ik_payment_queue_wrapper.dart @@ -0,0 +1,264 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:collection/collection.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:json_annotation/json_annotation.dart'; + +import '../../iap_kit_wrappers.dart'; +import '../channel.dart'; +import '../in_app_purchase_ohos_platform.dart'; + +part 'ik_payment_queue_wrapper.g.dart'; + +class IKPaymentQueueWrapper { + /// Returns the default payment queue. + /// + /// We do not support instantiating a custom payment queue, hence the + /// singleton. However, you can override the observer. + factory IKPaymentQueueWrapper() { + return _singleton; + } + + IKPaymentQueueWrapper._(); + + static final IKPaymentQueueWrapper _singleton = IKPaymentQueueWrapper._(); + + IKTransactionObserverWrapper? _observer; + + Future> transactions() async { + return _getTransactionList((await channel + .invokeListMethod('iap#transactions'))!); + } + + static Future queryEnvironmentStatus() async => + (await channel.invokeMethod('iap#queryEnvironmentStatus')) ?? false; + + void setTransactionObserver(IKTransactionObserverWrapper observer) { + _observer = observer; + channel.setMethodCallHandler(handleObserverCallbacks); + } + + Future startObservingTransactionQueue() => + channel.invokeMethod('iap#startObservingTransactionQueue'); + + Future stopObservingTransactionQueue() => + channel.invokeMethod('iap#stopObservingTransactionQueue'); + + Future addPayment(IKPaymentWrapper payment) async { + assert(_observer != null, + '[in_app_purchase]: Trying to add a payment without an observer. One must be set using `IKPaymentQueueWrapper.setTransactionObserver` before the app launches.'); + final Map requestMap = payment.toMap(); + await channel.invokeMethod( + 'iap#createPurchase', + requestMap, + ); + } + + Future finishTransaction( + IKPaymentTransactionWrapper transaction) async { + final Map requestMap = transaction.toFinishMap(); + await channel.invokeMethod( + 'iap#finishPurchase', + requestMap, + ); + } + + // 暂未实现 + Future restoreTransactions({String? applicationUserName}) async { + await channel.invokeMethod( + 'iap#restoreTransactions', applicationUserName); + } + + /// Triage a method channel call from the platform and triggers the correct observer method. + /// + /// This method is public for testing purposes only and should not be used + /// outside this class. + @visibleForTesting + Future handleObserverCallbacks(MethodCall call) async { + assert(_observer != null, + '[in_app_purchase]: (Fatal)The observer has not been set but we received a purchase transaction notification. Please ensure the observer has been set using `setTransactionObserver`. Make sure the observer is added right at the App Launch.'); + print('MethodCallHandlerImpl handleObserverCallbacks : ' + call.method); + final IKTransactionObserverWrapper observer = _observer!; + switch (call.method) { + case 'updatedTransactions': + { + print('MethodCallHandlerImpl handleObserverCallbacks'); + final List transactions = + _getTransactionList(call.arguments as List); + return Future(() { + observer.updatedTransactions(transactions: transactions); + }); + } + case 'removedTransactions': + { + final List transactions = + _getTransactionList(call.arguments as List); + return Future(() { + observer.removedTransactions(transactions: transactions); + }); + } + default: + break; + } + throw PlatformException( + code: 'no_such_callback', + message: 'Did not recognize the observer callback ${call.method}.'); + } + + // Get transaction wrapper object list from arguments. + List _getTransactionList( + List transactionsData) { + return transactionsData.map((dynamic map) { + return IKPaymentTransactionWrapper.fromJson( + Map.castFrom( + map as Map)); + }).toList(); + } +} + +@immutable +@JsonSerializable() +class IKError { + /// Creates a new [IKError] object with the provided information. + const IKError( + {required this.code, required this.domain, required this.userInfo}); + + /// Constructs an instance of this from a key-value map of data. + /// + /// The map needs to have named string keys with values matching the names and + /// types of all of the members on this class. The `map` parameter must not be + /// null. + factory IKError.fromJson(Map map) { + return _$IKErrorFromJson(map); + } + + /// Error code + @JsonKey(defaultValue: 0) + final int code; + + /// Error + @JsonKey(defaultValue: '') + final String domain; + + /// A map that contains more detailed information about the error. + @JsonKey(defaultValue: {}) + final Map userInfo; + + @override + bool operator ==(Object other) { + if (identical(other, this)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + return other is IKError && + other.code == code && + other.domain == domain && + const DeepCollectionEquality.unordered() + .equals(other.userInfo, userInfo); + } + + @override + int get hashCode => Object.hash( + code, + domain, + userInfo, + ); +} + +/// Dart wrapper around IAP Kit +/// [PurchaseParameter](https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/iap-iap-V5#section1340120344598). +/// +/// Used as the parameter to initiate a payment. In general, a developer should +/// not need to create the payment object explicitly; instead, use +/// [IKPaymentQueueWrapper.addPayment] directly with a product identifier to +/// initiate a payment. +@immutable +@JsonSerializable(createToJson: true) +class IKPaymentWrapper { + /// Creates a new [IKPaymentWrapper] with the provided information. + const IKPaymentWrapper({ + required this.productId, + this.productType, + this.developerPayload, + this.reservedInfo, + this.promotionalOfferId, + this.applicationUserName, + this.jwsRepresentation, + }); + + /// Constructs an instance of this from a key value map of data. + /// + /// The map needs to have named string keys with values matching the names and + /// types of all of the members on this class. The `map` parameter must not be + /// null. + factory IKPaymentWrapper.fromJson(Map map) { + return _$IKPaymentWrapperFromJson(map); + } + + /// Creates a Map object describes the payment object. + Map toMap() { + return { + 'productId': productId, + 'productType': productType, + 'developerPayload': developerPayload, + 'reservedInfo': reservedInfo, + 'promotionalOfferId': promotionalOfferId, + 'applicationUserName': applicationUserName, + 'jwsRepresentation': jwsRepresentation, + }; + } + + /// 待支付的商品ID。商品ID来源于开发者在AppGallery Connect中配置商品信息时设置的“商品ID”,具体请参见配置商品信息。 + final String productId; + + /// 需要查询的商品类型 + final ProductType? productType; + + /// 商户侧保留信息 + final String? developerPayload; + + /// 要求JSON String格式,商户可以将额外需要传入的字段以key-value的形式设置在JSON String中,并通过该参数传入。 + final String? reservedInfo; + + /// 优惠ID。优惠ID来源于开发者在AppGallery Connect中配置商品信息时设置的促销优惠标识符, + /// 具体请参见设置促销价格。传递该字段且要生效,需传递jwsRepresentation字段包含促销优惠信息。 + final String? promotionalOfferId; + + /// 用户账户相关联的混淆字符串,唯一标识用户。传递优惠ID场景,可以传递该字段。 + final String? applicationUserName; + + /// 包含购买参数信息的JWS格式签名数据。购买参数,如促销优惠等。 + final String? jwsRepresentation; + + @override + bool operator ==(Object other) { + if (identical(other, this)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + return other is IKPaymentWrapper && + other.productId == productId && + other.productType == productType && + other.developerPayload == developerPayload && + other.reservedInfo == reservedInfo && + other.promotionalOfferId == promotionalOfferId && + other.applicationUserName == applicationUserName && + other.jwsRepresentation == jwsRepresentation; + } + + @override + int get hashCode => Object.hash(productId, productType, developerPayload, + reservedInfo, promotionalOfferId); + + @override + String toString() => _$IKPaymentWrapperToJson(this).toString(); +} diff --git a/packages/in_app_purchase/in_app_purchase_ohos/lib/src/iap_kit_wrappers/ik_payment_queue_wrapper.g.dart b/packages/in_app_purchase/in_app_purchase_ohos/lib/src/iap_kit_wrappers/ik_payment_queue_wrapper.g.dart new file mode 100644 index 0000000000000000000000000000000000000000..1cd277f1c9b10a2b83274e66cb45d73c0070c4b3 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/lib/src/iap_kit_wrappers/ik_payment_queue_wrapper.g.dart @@ -0,0 +1,50 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'ik_payment_queue_wrapper.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +IKError _$IKErrorFromJson(Map json) => IKError( + code: json['code'] as int? ?? 0, + domain: json['domain'] as String? ?? '', + userInfo: (json['userInfo'] as Map?)?.map( + (k, e) => MapEntry(k as String, e), + ) ?? + {}, + ); + +IKPaymentWrapper _$IKPaymentWrapperFromJson(Map json) => IKPaymentWrapper( + productId: json['productId'] as String? ?? '', + productType: _$IKProductTypeFromInt(json['productType'] as int? ?? 0), + developerPayload: json['developerPayload'] as String?, + reservedInfo: json['reservedInfo'] as String?, + promotionalOfferId: json['promotionalOfferId'] as String?, + applicationUserName: json['applicationUserName'] as String?, + jwsRepresentation: json['jwsRepresentation'] as String?, + ); + +Map _$IKPaymentWrapperToJson(IKPaymentWrapper instance) => + { + 'productId': instance.productId, + 'productType': instance.productType, + 'developerPayload': instance.developerPayload, + 'reservedInfo': instance.reservedInfo, + 'promotionalOfferId': instance.promotionalOfferId, + 'applicationUserName': instance.applicationUserName, + 'jwsRepresentation': instance.jwsRepresentation, + }; + +ProductType _$IKProductTypeFromInt(int type) { + switch (type) { + case 0: + return ProductType.CONSUMABLE; + case 1: + return ProductType.NONCONSUMABLE; + case 3: + return ProductType.AUTORENEWABLE; + default: + return ProductType.CONSUMABLE; + } +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_ohos/lib/src/iap_kit_wrappers/ik_payment_transaction_wrappers.dart b/packages/in_app_purchase/in_app_purchase_ohos/lib/src/iap_kit_wrappers/ik_payment_transaction_wrappers.dart new file mode 100644 index 0000000000000000000000000000000000000000..dc34cd9551f989eb46340f5a0137c113df7f6c92 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/lib/src/iap_kit_wrappers/ik_payment_transaction_wrappers.dart @@ -0,0 +1,172 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/foundation.dart'; +import 'package:json_annotation/json_annotation.dart'; + +import 'enum_converters.dart'; +import 'ik_payment_queue_wrapper.dart'; +import 'ik_product_wrapper.dart'; + +part 'ik_payment_transaction_wrappers.g.dart'; + +/// Callback handlers for transaction status changes. +abstract class IKTransactionObserverWrapper { + /// Triggered when any transactions are updated. + void updatedTransactions( + {required List transactions}); + + /// Triggered when any transactions are removed from the payment queue. + void removedTransactions( + {required List transactions}); +} + +/// The state of a transaction. +enum IKPaymentTransactionStateWrapper { + /// Indicates the transaction is being processed in App Store. + /// + /// You should update your UI to indicate that you are waiting for the + /// transaction to update to another state. Never complete a transaction that + /// is still in a purchasing state. + @JsonValue(0) + purchasing, + + /// The user's payment has been succesfully processed. + /// + /// You should provide the user the content that they purchased. + @JsonValue(1) + purchased, + + /// The transaction failed. + /// + /// Check the [IKPaymentTransactionWrapper.error] property from + /// [IKPaymentTransactionWrapper] for details. + @JsonValue(2) + failed, + + /// This transaction is restoring content previously purchased by the user. + /// + /// The previous transaction information can be obtained in + /// [IKPaymentTransactionWrapper.originalTransaction] from + /// [IKPaymentTransactionWrapper]. + @JsonValue(3) + restored, + + /// The transaction is in the queue but pending external action. Wait for + /// another callback to get the final state. + /// + /// You should update your UI to indicate that you are waiting for the + /// transaction to update to another state. + @JsonValue(4) + deferred, + + /// Indicates the transaction is in an unspecified state. + @JsonValue(-1) + unspecified, +} + +/// Created when a payment is added to the [IKPaymentQueueWrapper]. +/// +/// Transactions are delivered to your app when a payment is finished +/// processing. Completed transactions provide a receipt and a transaction +/// identifier that the app can use to save a permanent record of the processed +/// payment. +@JsonSerializable(createToJson: true) +@immutable +class IKPaymentTransactionWrapper { + /// Creates a new [IKPaymentTransactionWrapper] with the provided information. + // TODO(stuartmorgan): Temporarily ignore const warning in other parts of the + // federated package, and remove this. + // ignore: prefer_const_constructors_in_immutables + IKPaymentTransactionWrapper({ + required this.payment, + required this.transactionState, + this.originalTransaction, + this.transactionTimeStamp, + this.transactionIdentifier, + this.error, + }); + + /// Constructs an instance of this from a key value map of data. + /// + /// The map needs to have named string keys with values matching the names and + /// types of all of the members on this class. The `map` parameter must not be + /// null. + factory IKPaymentTransactionWrapper.fromJson(Map map) { + return _$IKPaymentTransactionWrapperFromJson(map); + } + + /// Current transaction state. + @IKTransactionStatusConverter() + final IKPaymentTransactionStateWrapper transactionState; + + /// The payment that has been created and added to the payment queue which + /// generated this transaction. + final IKPaymentWrapper payment; + + /// The original Transaction. + /// + /// Only available if the [transactionState] is [SKPaymentTransactionStateWrapper.restored]. + /// Otherwise the value is `null`. + /// + /// When the [transactionState] + /// is [IKPaymentTransactionStateWrapper.restored], the current transaction + /// object holds a new [transactionIdentifier]. + final IKPaymentTransactionWrapper? originalTransaction; + + /// The timestamp of the transaction. + /// + /// Seconds since epoch. It is only defined when the [transactionState] is + /// [IKPaymentTransactionStateWrapper.purchased] or + /// [IKPaymentTransactionStateWrapper.restored]. + /// Otherwise, the value is `null`. + final double? transactionTimeStamp; + + /// The unique string identifer of the transaction. + /// + /// It is only defined when the [transactionState] is + /// [IKPaymentTransactionStateWrapper.purchased] or + /// [IKPaymentTransactionStateWrapper.restored]. You may wish to record this + /// string as part of an audit trail for App Store purchases. The value of + /// this string corresponds to the same property in the receipt. + /// + /// The value is `null` if it is an unsuccessful transaction. + final String? transactionIdentifier; + + /// The error object + /// + /// Only available if the [transactionState] is + /// [IKPaymentTransactionStateWrapper.failed]. + final IKError? error; + + @override + bool operator ==(Object other) { + if (identical(other, this)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + return other is IKPaymentTransactionWrapper && + other.payment == payment && + other.transactionState == transactionState && + other.originalTransaction == originalTransaction && + other.transactionTimeStamp == transactionTimeStamp && + other.transactionIdentifier == transactionIdentifier && + other.error == error; + } + + @override + int get hashCode => Object.hash(payment, transactionState, + originalTransaction, transactionTimeStamp, transactionIdentifier, error); + + @override + String toString() => _$IKPaymentTransactionWrapperToJson(this).toString(); + + /// The payload that is used to finish this transaction. + Map toFinishMap() => { + 'transactionIdentifier': transactionIdentifier, + 'productIdentifier': payment.productId, + }; +} diff --git a/packages/in_app_purchase/in_app_purchase_ohos/lib/src/iap_kit_wrappers/ik_payment_transaction_wrappers.g.dart b/packages/in_app_purchase/in_app_purchase_ohos/lib/src/iap_kit_wrappers/ik_payment_transaction_wrappers.g.dart new file mode 100644 index 0000000000000000000000000000000000000000..9a51a4267cada34f1173bf2cab2411e6fbf5fd6a --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/lib/src/iap_kit_wrappers/ik_payment_transaction_wrappers.g.dart @@ -0,0 +1,36 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'ik_payment_transaction_wrappers.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +IKPaymentTransactionWrapper _$IKPaymentTransactionWrapperFromJson(Map json) => + IKPaymentTransactionWrapper( + payment: IKPaymentWrapper.fromJson( + Map.from(json['payment'] as Map)), + transactionState: const IKTransactionStatusConverter() + .fromJson(json['transactionState'] as int?), + originalTransaction: json['originalTransaction'] == null + ? null + : IKPaymentTransactionWrapper.fromJson( + Map.from(json['originalTransaction'] as Map)), + transactionTimeStamp: (json['transactionTimeStamp'] as num?)?.toDouble(), + transactionIdentifier: json['transactionIdentifier'] as String?, + error: json['error'] == null + ? null + : IKError.fromJson(Map.from(json['error'] as Map)), + ); + +Map _$IKPaymentTransactionWrapperToJson( + IKPaymentTransactionWrapper instance) => + { + 'transactionState': const IKTransactionStatusConverter() + .toJson(instance.transactionState), + 'payment': instance.payment, + 'originalTransaction': instance.originalTransaction, + 'transactionTimeStamp': instance.transactionTimeStamp, + 'transactionIdentifier': instance.transactionIdentifier, + 'error': instance.error, + }; diff --git a/packages/in_app_purchase/in_app_purchase_ohos/lib/src/iap_kit_wrappers/ik_product_wrapper.dart b/packages/in_app_purchase/in_app_purchase_ohos/lib/src/iap_kit_wrappers/ik_product_wrapper.dart new file mode 100644 index 0000000000000000000000000000000000000000..057e60ac240a82cfa5087c397a93a5c61f773cfb --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/lib/src/iap_kit_wrappers/ik_product_wrapper.dart @@ -0,0 +1,279 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:collection/collection.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; +import 'package:json_annotation/json_annotation.dart'; +import 'enum_converters.dart'; + +// WARNING: Changes to `@JsonSerializable` classes need to be reflected in the +// below generated file. Run `flutter packages pub run build_runner watch` to +// rebuild and watch for further changes. +part 'ik_product_wrapper.g.dart'; + +/// Represents the response object returned by [IKRequestMaker.startProductRequest]. +/// Contains information about a list of products and a list of invalid product identifiers. +@JsonSerializable() +@immutable +class IKProductResponseWrapper { + /// Creates an [IKProductResponseWrapper] with the given product details. + // TODO(stuartmorgan): Temporarily ignore const warning in other parts of the + // federated package, and remove this. + // ignore: prefer_const_constructors_in_immutables + IKProductResponseWrapper( + {required this.products, required this.invalidProductIdentifiers}); + + /// Constructing an instance from a map from the Objective-C layer. + /// + /// This method should only be used with `map` values returned by [IKRequestMaker.startProductRequest]. + factory IKProductResponseWrapper.fromJson(Map map) { + var maptt = _$SkProductResponseWrapperFromJson(map); + return _$SkProductResponseWrapperFromJson(map); + } + + /// Stores all matching successfully found products. + /// + /// One product in this list matches one valid product identifier passed to the [IKRequestMaker.startProductRequest]. + /// Will be empty if the [IKRequestMaker.startProductRequest] method does not pass any correct product identifier. + @JsonKey(defaultValue: []) + final List products; + + /// Stores product identifiers in the `productIdentifiers` from [IKRequestMaker.startProductRequest] that are not recognized by the AppGallery. + /// Will be empty if all the product identifiers are valid. + @JsonKey(defaultValue: []) + final List invalidProductIdentifiers; + + @override + bool operator ==(Object other) { + if (identical(other, this)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + return other is IKProductResponseWrapper && + const DeepCollectionEquality().equals(other.products, products) && + const DeepCollectionEquality() + .equals(other.invalidProductIdentifiers, invalidProductIdentifiers); + } + + @override + int get hashCode => Object.hash(products, invalidProductIdentifiers); +} + +/// Used as a property in the [IKProductSubscriptionPeriodWrapper]. Minimum is a day and maximum is a year. +// The values of the enum options are matching the [IKProductPeriodUnit]'s values. Should there be an update or addition +// in the [IKProductPeriodUnit], this need to be updated to match. +enum IKSubscriptionPeriodUnit { + /// An interval lasting one day. + @JsonValue(0) + day, + + /// An interval lasting one month. + @JsonValue(1) + + /// An interval lasting one week. + week, + @JsonValue(2) + + /// An interval lasting one month. + month, + + /// An interval lasting one year. + @JsonValue(3) + year, +} + +/// A period is defined by a [numberOfUnits] and a [unit], e.g for a 3 months period [numberOfUnits] is 3 and [unit] is a month. +/// It is used as a property in [IKProductDiscountWrapper] and [IKProductWrapper]. +@JsonSerializable() +@immutable +class IKProductSubscriptionPeriodWrapper { + /// Creates an [IKProductSubscriptionPeriodWrapper] for a `numberOfUnits`x`unit` period. + // TODO(stuartmorgan): Temporarily ignore const warning in other parts of the + // federated package, and remove this. + // ignore: prefer_const_constructors_in_immutables + IKProductSubscriptionPeriodWrapper( + {required this.numberOfUnits, required this.unit}); + + /// Constructing an instance from a map from the Objective-C layer. + /// + /// This method should only be used with `map` values returned by [IKProductDiscountWrapper.fromJson] or [IKProductWrapper.fromJson]. + factory IKProductSubscriptionPeriodWrapper.fromJson( + Map? map) { + if (map == null) { + return IKProductSubscriptionPeriodWrapper( + numberOfUnits: 0, unit: IKSubscriptionPeriodUnit.day); + } + return _$IKProductSubscriptionPeriodWrapperFromJson(map); + } + + /// The number of [unit] units in this period. + /// + /// Must be greater than 0 if the object is valid. + @JsonKey(defaultValue: 0) + final int numberOfUnits; + + /// The time unit used to specify the length of this period. + @IKSubscriptionPeriodUnitConverter() + final IKSubscriptionPeriodUnit unit; + + @override + bool operator ==(Object other) { + if (identical(other, this)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + return other is IKProductSubscriptionPeriodWrapper && + other.numberOfUnits == numberOfUnits && + other.unit == unit; + } + + @override + int get hashCode => Object.hash(numberOfUnits, unit); +} + +/// This is used as a property in the [IKProductDiscountWrapper]. +// The values of the enum options are matching the [IKProductDiscountPaymentMode]'s values. Should there be an update or addition +// in the [IKProductDiscountPaymentMode], this need to be updated to match. +enum IKProductDiscountPaymentMode { + /// Allows user to pay the discounted price at each payment period. + @JsonValue(0) + payAsYouGo, + + /// Allows user to pay the discounted price upfront and receive the product for the rest of time that was paid for. + @JsonValue(1) + payUpFront, + + /// User pays nothing during the discounted period. + @JsonValue(2) + freeTrail, + + /// Unspecified mode. + @JsonValue(-1) + unspecified, +} + + +/// This is used as a property in the [IKProductDiscountWrapper]. +/// The values of the enum options are matching the [IKProductDiscountType]'s +/// values. +/// +/// Values representing the types of discount offers an app can present. +enum IKProductDiscountType { + /// A constant indicating the discount type is an introductory offer. + @JsonValue(0) + introductory, + + /// A constant indicating the discount type is a promotional offer. + @JsonValue(1) + subscription, +} + +/// Dart wrapper around IAP Kit [Product](https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/iap-iap-V5#section346874219313). +/// +/// A list of [IKProductWrapper] is returned in the [IKRequestMaker.startProductRequest] method, and +/// should be stored for use when making a payment. +@JsonSerializable() +@immutable +class IKProductWrapper { + /// Creates an [IKProductWrapper] with the given product details. + // TODO(stuartmorgan): Temporarily ignore const warning in other parts of the + // federated package, and remove this. + // ignore: prefer_const_constructors_in_immutables + IKProductWrapper({ + required this.id, + required this.type, + required this.name, + required this.description, + required this.localPrice, + required this.microPrice, + required this.originalLocalPrice, + required this.originalMicroPrice, + required this.currency, + this.status, + // this.subscriptionInfo, + // this.promotionalOffers, + this.jsonRepresentation, + }); + + /// Constructing an instance from a map from the ets layer. + /// + /// This method should only be used with `map` values returned by [IKProductResponseWrapper.fromJson]. + factory IKProductWrapper.fromJson(Map map) { + return _$IKProductWrapperFromJson(map); + } + + /// 商品ID + final String id; + + /// 商品类型 + final ProductType type; + + /// 商品名称,为配置商品信息时配置的名称。用于显示在应用内支付收银台 + final String name; + + /// 商品描述,即配置商品信息时配置的描述信息 + final String description; + + /// 商品的展示价格,包含商品币种和价格,格式为“币种+商品价格”,例如 EUR 0.15。部分国家/地区会返回“货币符号+商品价格”,例如中国大陆返回“¥0.15”。此价格含税。可选。 + final String localPrice; + + /// 商品实际价格乘以1,000,000后的微单位价格。例如某个商品实际价格是1.99美元,则该商品对应的微单位价格为:1.99*1000000=1990000。 + final int microPrice; + + /// 商品的原价,包含商品币种和价格,格式为“币种+商品价格”,例如 EUR 0.15。部分国家/地区会返回“货币符号+商品价格”,例如中国大陆返回“¥0.15”。此价格含税。 + final String originalLocalPrice; + + /// 商品原价的微单位价格。商品原价乘以1,000,000后的微单位价格。例如某个商品原价是1.99美元,则该商品对应的微单位价格为:1.99*1000000=1990000。 + final int originalMicroPrice; + + /// 用于支付该商品的币种,必须符合ISO 4217标准,例如USD、CNY、MYR。 + final String currency; + + /// 商品状态 + final ProductStatus? status; + + /// 自动续期订阅商品相关的信息。可选。 + // final SubscriptionInfo? subscriptionInfo; + + /// 订阅商品支持的优惠信息列表 + // final PromotionalOffer[]? promotionalOffers; + + /// 商品详细信息的原始JSON字符串 + final String? jsonRepresentation; +} + +/// 商品状态枚举 +enum ProductType { + /// 消耗型商品 + @JsonValue(0) + CONSUMABLE, + + /// 非消耗型商品 + @JsonValue(1) + NONCONSUMABLE, + + /// 自动续期订阅商品 + @JsonValue(3) + AUTORENEWABLE, +} + +/// 商品状态枚举 +enum ProductStatus { + /// 有效状态。 + @JsonValue(0) + VALID, + + /// 取消状态,即删除。此状态的商品不可续订,也不可订阅。 + @JsonValue(1) + CANCELED, + + /// 下线状态,不能订阅,但老用户仍可续订。 + @JsonValue(3) + OFFLINE, +} diff --git a/packages/in_app_purchase/in_app_purchase_ohos/lib/src/iap_kit_wrappers/ik_product_wrapper.g.dart b/packages/in_app_purchase/in_app_purchase_ohos/lib/src/iap_kit_wrappers/ik_product_wrapper.g.dart new file mode 100644 index 0000000000000000000000000000000000000000..072612e1c4be955a94c1a46b9a5073fc24575d9f --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/lib/src/iap_kit_wrappers/ik_product_wrapper.g.dart @@ -0,0 +1,69 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'ik_product_wrapper.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +IKProductResponseWrapper _$SkProductResponseWrapperFromJson(Map json) => + IKProductResponseWrapper( + products: (json['products'] as List?) + ?.map((e) => IKProductWrapper.fromJson( + Map.from(e as Map))) + .toList() ?? + [], + invalidProductIdentifiers: + (json['invalidProductIdentifiers'] as List?) + ?.map((e) => e as String) + .toList() ?? + [], + ); + +IKProductSubscriptionPeriodWrapper _$IKProductSubscriptionPeriodWrapperFromJson( + Map json) => + IKProductSubscriptionPeriodWrapper( + numberOfUnits: json['numberOfUnits'] as int? ?? 0, + unit: const IKSubscriptionPeriodUnitConverter() + .fromJson(json['unit'] as int?), + ); + +IKProductWrapper _$IKProductWrapperFromJson(Map json) => IKProductWrapper( + id: json['id'] as String? ?? '', + type: _$IKProductTypeFromInt(json['type'] as int? ?? 0), + name: json['name'] as String? ?? '', + description: json['description'] as String? ?? '', + localPrice: json['localPrice'] as String? ?? '', + microPrice: json['microPrice'] as int? ?? 0, + originalLocalPrice: json['originalLocalPrice'] as String? ?? '', + originalMicroPrice: json['originalMicroPrice'] as int? ?? 0, + currency: json['currency'] as String? ?? '', + status: _$IKProductStatusFromInt(json['status'] as int? ?? 0), + jsonRepresentation: json['jsonRepresentation'] as String?, + ); + +ProductType _$IKProductTypeFromInt(int type) { + switch (type) { + case 0: + return ProductType.CONSUMABLE; + case 1: + return ProductType.NONCONSUMABLE; + case 3: + return ProductType.AUTORENEWABLE; + default: + return ProductType.CONSUMABLE; + } +} + +ProductStatus _$IKProductStatusFromInt(int type) { + switch (type) { + case 0: + return ProductStatus.VALID; + case 1: + return ProductStatus.CANCELED; + case 3: + return ProductStatus.OFFLINE; + default: + return ProductStatus.VALID; + } +} diff --git a/packages/in_app_purchase/in_app_purchase_ohos/lib/src/iap_kit_wrappers/ik_receipt_manager.dart b/packages/in_app_purchase/in_app_purchase_ohos/lib/src/iap_kit_wrappers/ik_receipt_manager.dart new file mode 100644 index 0000000000000000000000000000000000000000..68feb9e103623e4e13d1ad42b35b99c07651126c --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/lib/src/iap_kit_wrappers/ik_receipt_manager.dart @@ -0,0 +1,18 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import '../channel.dart'; + +// ignore: avoid_classes_with_only_static_members +/// This class contains static methods to manage StoreKit receipts. +class IKReceiptManager { + /// Retrieve the receipt data from your application's main bundle. + static Future retrieveReceiptData() async { + return (await channel.invokeMethod( + 'iap#retrieveReceiptData')) ?? + ''; + } +} diff --git a/packages/in_app_purchase/in_app_purchase_ohos/lib/src/iap_kit_wrappers/ik_request_maker.dart b/packages/in_app_purchase/in_app_purchase_ohos/lib/src/iap_kit_wrappers/ik_request_maker.dart new file mode 100644 index 0000000000000000000000000000000000000000..0eebfe8362bb047f46293948d574d3bccc96e71b --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/lib/src/iap_kit_wrappers/ik_request_maker.dart @@ -0,0 +1,46 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/services.dart'; + +import '../channel.dart'; +import 'ik_product_wrapper.dart'; + +/// A request maker that handles all the requests made by IKRequest subclasses. +class IKRequestMaker { + /// Fetches product information for a list of given product identifiers. + /// + /// The `productIdentifiers` should contain legitimate product identifiers that you declared for the products in the iTunes Connect. Invalid identifiers + /// will be stored and returned in [SkProductResponseWrapper.invalidProductIdentifiers]. Duplicate values in `productIdentifiers` will be omitted. + /// If `productIdentifiers` is null, an `storekit_invalid_argument` error will be returned. If `productIdentifiers` is empty, a [SkProductResponseWrapper] + /// will still be returned with [SkProductResponseWrapper.products] being null. + /// + /// [SkProductResponseWrapper] is returned if there is no error during the request. + /// A [PlatformException] is thrown if the platform code making the request fails. + Future startProductRequest( + List productIdentifiers) async { + final Map? productResponseMap = + await channel.invokeMapMethod( + 'iap#queryProducts', + productIdentifiers, + ); + if (productResponseMap == null) { + throw PlatformException( + code: 'storekit_no_response', + message: 'StoreKit: Failed to get response from platform.', + ); + } + return IKProductResponseWrapper.fromJson(productResponseMap); + } + + Future startRefreshReceiptRequest( + {Map? receiptProperties}) { + return channel.invokeMethod( + 'iap#refreshReceipt', + receiptProperties, + ); + } +} diff --git a/packages/in_app_purchase/in_app_purchase_ohos/lib/src/in_app_purchase_ohos_platform.dart b/packages/in_app_purchase/in_app_purchase_ohos/lib/src/in_app_purchase_ohos_platform.dart new file mode 100644 index 0000000000000000000000000000000000000000..2fa17d603b1554f8269d990456733526a027e372 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/lib/src/in_app_purchase_ohos_platform.dart @@ -0,0 +1,219 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart'; + +import '../iap_kit_wrappers.dart'; +import '../in_app_purchase_ohos.dart'; + +/// [IAPError.code] code for failed purchases. +const String kPurchaseErrorCode = 'purchase_error'; + +/// Indicates store front is AppGallery. +const String kIAPSource = 'app_gallery'; + +/// An [InAppPurchasePlatform] that wraps IAP Kit. +/// +/// This translates various `IAP Kit` calls and responses into the +/// generic plugin API. +class InAppPurchaseOhosPlatform extends InAppPurchasePlatform { + /// Creates an [InAppPurchaseOhosPlatform] object. + /// + /// This constructor should only be used for testing, for any other purpose + /// get the connection from the [instance] getter. + @visibleForTesting + InAppPurchaseOhosPlatform(); + + static late IKPaymentQueueWrapper _skPaymentQueueWrapper; + static late _TransactionObserver _observer; + + @override + Stream> get purchaseStream => + _observer.purchaseUpdatedController.stream; + + /// Callback handler for transaction status changes. + @visibleForTesting + static IKTransactionObserverWrapper get observer => _observer; + + /// Registers this class as the default instance of [InAppPurchasePlatform]. + static void registerPlatform() { + // Register the [InAppPurchaseOhosPlatformAddition] containing + // IapKit-specific functionality. + InAppPurchasePlatformAddition.instance = + InAppPurchaseOhosPlatformAddition(); + + // Register the platform-specific implementation of the idiomatic + // InAppPurchase API. + InAppPurchasePlatform.instance = InAppPurchaseOhosPlatform(); + + _skPaymentQueueWrapper = IKPaymentQueueWrapper(); + + // Create a purchaseUpdatedController and notify the native side when to + // start of stop sending updates. + final StreamController> updateController = + StreamController>.broadcast( + onListen: () => _skPaymentQueueWrapper.startObservingTransactionQueue(), + onCancel: () => _skPaymentQueueWrapper.stopObservingTransactionQueue(), + ); + _observer = _TransactionObserver(updateController); + _skPaymentQueueWrapper.setTransactionObserver(observer); + } + + @override + Future isAvailable() => IKPaymentQueueWrapper.queryEnvironmentStatus(); + + @override + Future buyNonConsumable({required PurchaseParam purchaseParam}) async { + await _skPaymentQueueWrapper.addPayment( + IKPaymentWrapper(productId: purchaseParam.productDetails.id)); + return true; // There's no error feedback from ohos here to return. + } + + @override + Future buyConsumable( + {required PurchaseParam purchaseParam, bool autoConsume = true}) { + assert(autoConsume == true, 'On ohos, we should always auto consume'); + return buyNonConsumable(purchaseParam: purchaseParam); + } + + @override + Future completePurchase(PurchaseDetails purchase) { + assert( + purchase is AppGalleryPurchaseDetails, + 'On ohos, the `purchase` should always be of type `AppGalleryPurchaseDetails`.', + ); + + return _skPaymentQueueWrapper.finishTransaction( + (purchase as AppGalleryPurchaseDetails).ikPaymentTransaction, + ); + } + + @override + Future restorePurchases({String? applicationUserName}) async { + return _observer + .restoreTransactions( + queue: _skPaymentQueueWrapper, + applicationUserName: applicationUserName) + .whenComplete(() => _observer.cleanUpRestoredTransactions()); + } + + /// Query the product detail list. + /// + /// This method only returns [ProductDetailsResponse]. + /// To get detailed Store Kit product list, use [SkProductResponseWrapper.startProductRequest] + /// to get the [IKProductResponseWrapper]. + @override + Future queryProductDetails( + Set identifiers) async { + final IKRequestMaker requestMaker = IKRequestMaker(); + IKProductResponseWrapper response; + PlatformException? exception; + try { + response = await requestMaker.startProductRequest(identifiers.toList()); + print('InAppPurchasePlugin productDetailResponse response'); + } on PlatformException catch (e) { + exception = e; + response = IKProductResponseWrapper( + products: const [], + invalidProductIdentifiers: identifiers.toList()); + } + List productDetails = + []; + productDetails = response.products + .map((IKProductWrapper productWrapper) => + AppGalleryProductDetails.fromIKProduct(productWrapper)) + .toList(); + List invalidIdentifiers = response.invalidProductIdentifiers; + if (productDetails.isEmpty) { + invalidIdentifiers = identifiers.toList(); + } + final ProductDetailsResponse productDetailsResponse = + ProductDetailsResponse( + productDetails: productDetails, + notFoundIDs: invalidIdentifiers, + error: exception == null + ? null + : IAPError( + source: kIAPSource, + code: exception.code, + message: exception.message ?? '', + details: exception.details), + ); + return productDetailsResponse; + } +} + +enum _TransactionRestoreState { + notRunning, + waitingForTransactions, + receivedTransaction, +} + +class _TransactionObserver implements IKTransactionObserverWrapper { + _TransactionObserver(this.purchaseUpdatedController); + + final StreamController> purchaseUpdatedController; + + Completer? _restoreCompleter; + late String _receiptData; + _TransactionRestoreState _transactionRestoreState = + _TransactionRestoreState.notRunning; + + Future restoreTransactions({ + required IKPaymentQueueWrapper queue, + String? applicationUserName, + }) { + _transactionRestoreState = _TransactionRestoreState.waitingForTransactions; + _restoreCompleter = Completer(); + queue.restoreTransactions(applicationUserName: applicationUserName); + return _restoreCompleter!.future; + } + + void cleanUpRestoredTransactions() { + _restoreCompleter = null; + } + + @override + void updatedTransactions( + {required List transactions}) { + _handleTransationUpdates(transactions); + } + + @override + void removedTransactions( + {required List transactions}) {} + + Future getReceiptData() async { + try { + _receiptData = await IKReceiptManager.retrieveReceiptData(); + } catch (e) { + _receiptData = ''; + } + return _receiptData; + } + + Future _handleTransationUpdates( + List transactions) async { + print('MethodCallHandlerImpl _handleTransationUpdates '); + if (_transactionRestoreState == + _TransactionRestoreState.waitingForTransactions && + transactions.any((IKPaymentTransactionWrapper transaction) => + transaction.transactionState == + IKPaymentTransactionStateWrapper.restored)) { + _transactionRestoreState = _TransactionRestoreState.receivedTransaction; + } + + final String receiptData = await getReceiptData(); + final List purchases = transactions + .map((IKPaymentTransactionWrapper transaction) => + AppGalleryPurchaseDetails.fromIKTransaction( + transaction, receiptData)) + .toList(); + purchaseUpdatedController.add(purchases); + } +} diff --git a/packages/in_app_purchase/in_app_purchase_ohos/lib/src/in_app_purchase_ohos_platform_addition.dart b/packages/in_app_purchase/in_app_purchase_ohos/lib/src/in_app_purchase_ohos_platform_addition.dart new file mode 100644 index 0000000000000000000000000000000000000000..88a0325358931fc30d47a145df61ea217e637344 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/lib/src/in_app_purchase_ohos_platform_addition.dart @@ -0,0 +1,33 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart'; + +import '../iap_kit_wrappers.dart'; +import '../in_app_purchase_ohos.dart'; + +/// Contains InApp Purchase features that are only available on ohos. +class InAppPurchaseOhosPlatformAddition + extends InAppPurchasePlatformAddition { + + /// Retry loading purchase data after an initial failure. + /// + /// If no results, a `null` value is returned. + Future refreshPurchaseVerificationData() async { + await IKRequestMaker().startRefreshReceiptRequest(); + try { + final String receipt = await IKReceiptManager.retrieveReceiptData(); + return PurchaseVerificationData( + localVerificationData: receipt, + serverVerificationData: receipt, + source: kIAPSource); + } catch (e) { + // ignore: avoid_print + print( + 'Something is wrong while fetching the receipt, this normally happens when the app is ' + 'running on a simulator: $e'); + return null; + } + } +} diff --git a/packages/in_app_purchase/in_app_purchase_ohos/lib/src/types/app_gallery_product_details.dart b/packages/in_app_purchase/in_app_purchase_ohos/lib/src/types/app_gallery_product_details.dart new file mode 100644 index 0000000000000000000000000000000000000000..907dd328854d84c7c96c39d18cc53aaa19823d8b --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/lib/src/types/app_gallery_product_details.dart @@ -0,0 +1,43 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart'; + +import '../../iap_kit_wrappers.dart'; + +/// The class represents the information of a product as registered in the AppGallery Connect +/// AppGallery. +class AppGalleryProductDetails extends ProductDetails { + /// Creates a new AppStore specific product details object with the provided + /// details. + AppGalleryProductDetails({ + required super.id, + required super.title, + required super.description, + required super.price, + required super.rawPrice, + required super.currencyCode, + required this.skProduct, + required super.currencySymbol, + }); + + factory AppGalleryProductDetails.fromIKProduct(IKProductWrapper product) { + return AppGalleryProductDetails( + id: product.id, + title: product.name, + description: product.description, + price: product.localPrice, + rawPrice: product.microPrice / 1000000.0, + currencyCode: product.currency, + currencySymbol: product.localPrice.isNotEmpty + ? product.localPrice.replaceAll(RegExp(r'[0-9.]+'), "") + : product.currency, + skProduct: product, + ); + } + + /// Points back to the [IKProductWrapper] object that was used to generate + /// this [AppGalleryProductDetails] object. + final IKProductWrapper skProduct; +} diff --git a/packages/in_app_purchase/in_app_purchase_ohos/lib/src/types/app_gallery_purchase_details.dart b/packages/in_app_purchase/in_app_purchase_ohos/lib/src/types/app_gallery_purchase_details.dart new file mode 100644 index 0000000000000000000000000000000000000000..b77ba6d569827b1b2e28f2510702aa1f5396d307 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/lib/src/types/app_gallery_purchase_details.dart @@ -0,0 +1,79 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart'; + +import '../../iap_kit_wrappers.dart'; +import '../../in_app_purchase_ohos.dart'; +import '../iap_kit_wrappers/enum_converters.dart'; + +/// The class represents the information of a purchase made with the IAP Kit +/// AppGallery. +class AppGalleryPurchaseDetails extends PurchaseDetails { + /// Creates a new AppStore specific purchase details object with the provided + /// details. + AppGalleryPurchaseDetails({ + super.purchaseID, + required super.productID, + required super.verificationData, + required super.transactionDate, + required this.ikPaymentTransaction, + required PurchaseStatus status, + }) : super(status: status) { + this.status = status; + } + + factory AppGalleryPurchaseDetails.fromIKTransaction( + IKPaymentTransactionWrapper transaction, + String base64EncodedReceipt, + ) { + print('MethodCallHandlerImpl AppGalleryPurchaseDetails.fromIKTransaction '); + final AppGalleryPurchaseDetails purchaseDetails = AppGalleryPurchaseDetails( + productID: transaction.payment.productId, + purchaseID: transaction.transactionIdentifier, + ikPaymentTransaction: transaction, + status: const IKTransactionStatusConverter() + .toPurchaseStatus(transaction.transactionState, transaction.error), + transactionDate: transaction.transactionTimeStamp != null + ? (transaction.transactionTimeStamp! * 1000).toInt().toString() + : null, + verificationData: PurchaseVerificationData( + localVerificationData: base64EncodedReceipt, + serverVerificationData: base64EncodedReceipt, + source: kIAPSource), + ); + var statuus = purchaseDetails.status; + if (purchaseDetails.status == PurchaseStatus.error || + purchaseDetails.status == PurchaseStatus.canceled) { + purchaseDetails.error = IAPError( + source: kIAPSource, + code: kPurchaseErrorCode, + message: transaction.error?.domain ?? '', + details: transaction.error?.userInfo, + ); + } + return purchaseDetails; + } + + /// Points back to the [IKPaymentTransactionWrapper] which was used to + /// generate this [AppStorePurchaseDetails] object. + final IKPaymentTransactionWrapper ikPaymentTransaction; + + late PurchaseStatus _status; + + /// The status that this [PurchaseDetails] is currently on. + @override + PurchaseStatus get status => _status; + + @override + set status(PurchaseStatus status) { + _pendingCompletePurchase = status == PurchaseStatus.purchased; + _status = status; + } + + bool _pendingCompletePurchase = false; + + @override + bool get pendingCompletePurchase => _pendingCompletePurchase; +} diff --git a/packages/in_app_purchase/in_app_purchase_ohos/lib/src/types/app_gallery_purchase_param.dart b/packages/in_app_purchase/in_app_purchase_ohos/lib/src/types/app_gallery_purchase_param.dart new file mode 100644 index 0000000000000000000000000000000000000000..556acab0925a298d23dd341a6b1bb65bad05fa7a --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/lib/src/types/app_gallery_purchase_param.dart @@ -0,0 +1,18 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart'; + +/// Apple AppStore specific parameter object for generating a purchase. +class AppGalleryPurchaseParam extends PurchaseParam { + /// Creates a new [AppGalleryPurchaseParam] object with the given data. + AppGalleryPurchaseParam({ + required super.productDetails, + super.applicationUserName, + this.quantity = 1, + }); + + /// Quantity of the product user requested to buy. + final int quantity; +} diff --git a/packages/in_app_purchase/in_app_purchase_ohos/lib/src/types/types.dart b/packages/in_app_purchase/in_app_purchase_ohos/lib/src/types/types.dart new file mode 100644 index 0000000000000000000000000000000000000000..1704a2e9ca59ead77b8dcdf50682906db026f383 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/lib/src/types/types.dart @@ -0,0 +1,7 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +export 'app_gallery_product_details.dart'; +export 'app_gallery_purchase_details.dart'; +export 'app_gallery_purchase_param.dart'; diff --git a/packages/in_app_purchase/in_app_purchase_ohos/ohos/.gitignore b/packages/in_app_purchase/in_app_purchase_ohos/ohos/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..e2713a2779c5a3e0eb879efe6115455592caeea5 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/ohos/.gitignore @@ -0,0 +1,6 @@ +/node_modules +/oh_modules +/.preview +/build +/.cxx +/.test \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_ohos/ohos/BuildProfile.ets b/packages/in_app_purchase/in_app_purchase_ohos/ohos/BuildProfile.ets new file mode 100644 index 0000000000000000000000000000000000000000..fde84590b4cebb8b75f4d038e944f120c643fe2c --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/ohos/BuildProfile.ets @@ -0,0 +1,32 @@ +/* +* Copyright (c) 2024 SwanLink (Jiangsu) Technology Development Co., LTD. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/** + * Use these variables when you tailor your ArkTS code. They must be of the const type. + */ +export const HAR_VERSION = '1.0.0'; +export const BUILD_MODE_NAME = 'debug'; +export const DEBUG = true; +export const TARGET_NAME = 'default'; + +/** + * BuildProfile Class is used only for compatibility purposes. + */ +export default class BuildProfile { + static readonly HAR_VERSION = HAR_VERSION; + static readonly BUILD_MODE_NAME = BUILD_MODE_NAME; + static readonly DEBUG = DEBUG; + static readonly TARGET_NAME = TARGET_NAME; +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_ohos/ohos/Index.ets b/packages/in_app_purchase/in_app_purchase_ohos/ohos/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..8e39f7c8d0372cd1c4654dec5429fb2317a5740e --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/ohos/Index.ets @@ -0,0 +1,17 @@ +/* +* Copyright (c) 2024 SwanLink (Jiangsu) Technology Development Co., LTD. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import { InAppPurchasePlugin } from './src/main/ets/components/InAppPurchasePlugin' +export default InAppPurchasePlugin \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_ohos/ohos/build-profile.json5 b/packages/in_app_purchase/in_app_purchase_ohos/ohos/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..e6773f9f5d76a66d6d19fddc9c6ddb3f5621d3b1 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/ohos/build-profile.json5 @@ -0,0 +1,31 @@ +{ + "apiType": "stageMode", + "buildOption": { + }, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": false, + "files": [ + "./obfuscation-rules.txt" + ] + }, + "consumerFiles": [ + "./consumer-rules.txt" + ] + } + }, + }, + ], + "targets": [ + { + "name": "default" + }, + { + "name": "ohosTest" + } + ] +} diff --git a/packages/in_app_purchase/in_app_purchase_ohos/ohos/consumer-rules.txt b/packages/in_app_purchase/in_app_purchase_ohos/ohos/consumer-rules.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/packages/in_app_purchase/in_app_purchase_ohos/ohos/hvigorfile.ts b/packages/in_app_purchase/in_app_purchase_ohos/ohos/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..12a327db0aeeb9462eb1dc098f5e808df40e3dce --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/ohos/hvigorfile.ts @@ -0,0 +1,21 @@ +/* +* Copyright (c) 2024 SwanLink (Jiangsu) Technology Development Co., LTD. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import { harTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: harTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/packages/in_app_purchase/in_app_purchase_ohos/ohos/obfuscation-rules.txt b/packages/in_app_purchase/in_app_purchase_ohos/ohos/obfuscation-rules.txt new file mode 100644 index 0000000000000000000000000000000000000000..272efb6ca3f240859091bbbfc7c5802d52793b0b --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/ohos/obfuscation-rules.txt @@ -0,0 +1,23 @@ +# Define project specific obfuscation rules here. +# You can include the obfuscation configuration files in the current module's build-profile.json5. +# +# For more details, see +# https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/source-obfuscation-V5 + +# Obfuscation options: +# -disable-obfuscation: disable all obfuscations +# -enable-property-obfuscation: obfuscate the property names +# -enable-toplevel-obfuscation: obfuscate the names in the global scope +# -compact: remove unnecessary blank spaces and all line feeds +# -remove-log: remove all console.* statements +# -print-namecache: print the name cache that contains the mapping from the old names to new names +# -apply-namecache: reuse the given cache file + +# Keep options: +# -keep-property-name: specifies property names that you want to keep +# -keep-global-name: specifies names that you want to keep in the global scope + +-enable-property-obfuscation +-enable-toplevel-obfuscation +-enable-filename-obfuscation +-enable-export-obfuscation \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_ohos/ohos/oh-package.json5 b/packages/in_app_purchase/in_app_purchase_ohos/ohos/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..4c93b2c90164676de09d712d7ba79512dfac8961 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/ohos/oh-package.json5 @@ -0,0 +1,11 @@ +{ + "name": "in_app_purchase", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "Index.ets", + "author": "", + "license": "Apache-2.0", + "dependencies": { + "@ohos/flutter_ohos": "file:../libs/flutter.har" + } +} diff --git a/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/main/ets/common/JWTUtil.ts b/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/main/ets/common/JWTUtil.ts new file mode 100644 index 0000000000000000000000000000000000000000..6f87f4dbc22f2e9acb972ac3ac0b769e3e6b5bbd --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/main/ets/common/JWTUtil.ts @@ -0,0 +1,60 @@ +/* +* Copyright (c) 2024 Huawei Technologies Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +import { util } from '@kit.ArkTS'; + +const centerLineRegex: RegExp = new RegExp('-', 'g'); +const underLineRegex: RegExp = new RegExp('_', 'g'); +const textDecoder = util.TextDecoder.create("utf-8", { ignoreBOM: true }); +const base64 = new util.Base64Helper(); +const TAG: string = 'JWTUtil'; + +const BASE64_PAD_MOD = 4; +const BASE64_PAD_INVALID = 1; + +export class JWTUtil { + public static base64Decode(input: string) { + return textDecoder.decodeWithStream(base64.decodeSync(input)); + } + + private static base64UrlDecode(input: string) { + input = input + .replace(centerLineRegex, '+') + .replace(underLineRegex, '/'); + + // Pad out with standard base64 required padding characters + const pad = input.length % BASE64_PAD_MOD; + if (pad) { + if (pad === BASE64_PAD_INVALID) { + throw new Error('InvalidLengthError: Input base64url string is the wrong length to determine padding'); + } + input += new Array(5 - pad).join('='); + } + return this.base64Decode(input); + } + + public static decodeJwtObj(data: string) { + let jwt: string[] = data.split("."); + let exp: string = ""; + if (jwt.length < 3) { + return exp; + } + try { + exp = JWTUtil.base64UrlDecode(jwt[1]); + } catch (err) { + console.error(TAG, 'decodeJwtObj parse err: ' + JSON.stringify(err)); + } + return exp; + } +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/main/ets/common/TsUtil.ts b/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/main/ets/common/TsUtil.ts new file mode 100644 index 0000000000000000000000000000000000000000..17ddb00ee72c59457f8c257ef9175c139ed6720f --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/main/ets/common/TsUtil.ts @@ -0,0 +1,24 @@ +/* +* Copyright (c) 2024 SwanLink (Jiangsu) Technology Development Co., LTD. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +export function ObjToMap(obj: Object): Map { + const map = new Map(); + for (const key in obj) { + if (obj.hasOwnProperty(key)) { + map.set(key, obj[key]); + } + } + return map +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/main/ets/components/InAppPurchasePlugin.ets b/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/main/ets/components/InAppPurchasePlugin.ets new file mode 100644 index 0000000000000000000000000000000000000000..acc2a6683ac2115b91ceb8988fd05dc248296b34 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/main/ets/components/InAppPurchasePlugin.ets @@ -0,0 +1,69 @@ +/* +* Copyright (c) 2024 SwanLink (Jiangsu) Technology Development Co., LTD. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import { + AbilityAware, + AbilityPluginBinding, + FlutterPlugin, + FlutterPluginBinding, + BinaryMessenger, + Log, + MethodChannel, +} from '@ohos/flutter_ohos'; +import { common } from '@kit.AbilityKit'; +import { MethodCallHandlerImpl } from './MethodCallHandlerImpl'; + +const TAG = "InAppPurchasePlugin" + +export class InAppPurchasePlugin implements FlutterPlugin, AbilityAware { + private methodChannel: MethodChannel | null = null + private methodCallHandler: MethodCallHandlerImpl | null = null + private context: common.UIAbilityContext | null = null + + onAttachedToEngine(binding: FlutterPluginBinding): void { + this.setUpMethodChannel(binding.getBinaryMessenger(), binding.getApplicationContext()); + } + + onDetachedFromEngine(binding: FlutterPluginBinding): void { + this.teardownMethodChannel(); + } + + onAttachedToAbility(binding: AbilityPluginBinding): void { + this.context = binding.getAbility().context + this.methodCallHandler?.setContext(this.context) + } + + onDetachedFromAbility(): void { + this.methodCallHandler?.setContext(null) + } + + getUniqueClassName(): string { + return TAG + } + + setUpMethodChannel(messenger: BinaryMessenger, context: common.Context) { + this.methodChannel = new MethodChannel(messenger, "plugins.flutter.io/in_app_purchase"); + this.methodCallHandler = + new MethodCallHandlerImpl(context, this.methodChannel); + this.methodChannel.setMethodCallHandler(this.methodCallHandler); + } + + teardownMethodChannel() { + this.methodChannel?.setMethodCallHandler(null); + this.methodChannel = null; + this.methodCallHandler = null; + this.context = null; + } +} diff --git a/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/main/ets/components/MethodCallHandlerImpl.ets b/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/main/ets/components/MethodCallHandlerImpl.ets new file mode 100644 index 0000000000000000000000000000000000000000..28b3aeff9beb0345c645ce9998560b3a35ba54b1 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/main/ets/components/MethodCallHandlerImpl.ets @@ -0,0 +1,288 @@ +/* +* Copyright (c) 2024 SwanLink (Jiangsu) Technology Development Co., LTD. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import { MethodCall, MethodCallHandler, MethodChannel, MethodResult, Log, Any } from '@ohos/flutter_ohos'; +import { MethodNames } from './MethodNames'; +import { iap } from '@kit.IAPKit'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { common } from '@kit.AbilityKit'; +import { ProductType } from './ProductType'; +import { JWTUtil } from '../common/JWTUtil'; +import { PurchaseOrderPayload, PurchaseSubGroupStatusPayload } from './PurchaseOrderPayload'; +import { HashMap } from '@kit.ArkTS'; +import { TransactionState } from './TransactionState'; +import { PurchaseData } from './PurchaseData'; +import { ObjToMap } from '../common/TsUtil'; + +const TAG = "MethodCallHandlerImpl" + +export class IKError { + code: number = 0 + domain: string = '' + userInfo: Map = new Map() +} + +export class MethodCallHandlerImpl implements MethodCallHandler { + private context: common.UIAbilityContext | null = null + private methodChannel: MethodChannel + private observer: MethodChannel | null = null + private productDetailsMap: HashMap = new HashMap() + + constructor(context: common.Context, methodChannel: MethodChannel) { + this.methodChannel = methodChannel + } + + setContext(context: common.UIAbilityContext | null) { + this.context = context + } + + onMethodCall(call: MethodCall, result: MethodResult): void { + switch (call.method) { + case MethodNames.START_OBSERVING_TRANSACTION_QUEUE: + this.startObserver() + break; + case MethodNames.STOP_OBSERVING_TRANSACTION_QUEUE: + this.stopObserver() + break; + case MethodNames.QUERY_ENVIRONMENT_STATUS: + this.isFeatureSupported(result) + break; + case MethodNames.QUERY_PRODUCTS: + let productList: Array = call.args as Array ?? [] + this.queryProductDetails(productList, result) + break; + case MethodNames.CREATE_PURCHASE: + this.purchaseGoods(call.args, result) + break; + case MethodNames.RETRIEVE_RECEIPT_DATA: + result.success(null) + break; + case MethodNames.FINISH_PURCHASE: + this.iapFinishPurchase(call, result) + break; + default: + result.notImplemented(); + } + } + + isFeatureSupported(result: MethodResult): void { + try { + iap.queryEnvironmentStatus(this.context).then(() => { + result.success(true); + }).catch((err: BusinessError) => { + Log.e(TAG, `Failed to query environment status. Code is ${err.code}, message is ${err.message}`); + result.success(false); + }) + } catch (e) { + Log.e(TAG, `Failed to query environment status. err: ${JSON.stringify(e)}`); + result.success(false); + } + } + + queryProductDetails(list: Array, result: MethodResult) { + let productList: Array> = [] + let count: number = 0 + let validProductsList: Array = [] + let invalidProductIdentifiers: Array = [] + ProductType.forEach((type) => { + const queryProductParam: iap.QueryProductsParameter = { + productType: type, + productIds: list + }; + iap.queryProducts(this.context, queryProductParam).then((result) => { + let resultTemp = result.map((value) => { + validProductsList.push(value.id) + this.productDetailsMap.set(value.id, value) + return ObjToMap(value) + }) + productList.push(...resultTemp) + }).catch((err: BusinessError) => { + Log.e(TAG, `Failed to query products. Code is ${err.code}, message is ${err.message}`); + }).finally(() => { + count++ + if (count === ProductType.length) { + let productDetailsResponse: HashMap = new HashMap() + productDetailsResponse.set('products', productList) + list.forEach((id) => { + if (!validProductsList.includes(id)) { + invalidProductIdentifiers.push(id) + } + }) + productDetailsResponse.set('invalidProductIdentifiers', invalidProductIdentifiers) + result.success(productDetailsResponse) + } + }) + }) + } + + purchaseGoods(args: Any, result: MethodResult) { + let map: Map = args as Map + let id: string = map.get('productId') as string + // 拉起支付接口前更新状态为支付中 + let mapRes: Map = new Map() + mapRes.set('payment', args) + mapRes.set('transactionState', TransactionState.purchasing) + this.observer?.invokeMethod('updatedTransactions', [mapRes]) + this.createPurchase(id, map, result) + } + + createPurchase(id: string, args: Map, result: MethodResult) { + try { + const type = this.productDetailsMap.get(id).type + let createPurchaseParam: iap.PurchaseParameter = { + productId: id, + productType: type, + } + iap.createPurchase(this.context, createPurchaseParam).then(async (createPurchaseResult) => { + let type: number = JSON.parse(createPurchaseResult.purchaseData).type + if (type == iap.ProductType.AUTORENEWABLE) { + let jwsSubscriptionStatus: string = JSON.parse(createPurchaseResult.purchaseData).jwsSubscriptionStatus; + let purchaseStr = JWTUtil.decodeJwtObj(jwsSubscriptionStatus); + let purchaseOrderPayload = JSON.parse(purchaseStr) as PurchaseSubGroupStatusPayload; + let map: Map = new Map(); + map.set('payment', args) + map.set('transactionIdentifier', + purchaseOrderPayload.lastSubscriptionStatus?.lastPurchaseOrder.purchaseOrderId) + map.set('transactionState', TransactionState.purchased) + this.observer?.invokeMethod('updatedTransactions', [map]) + } else if (type == iap.ProductType.CONSUMABLE || type == iap.ProductType.NONCONSUMABLE) { + let jwsPurchaseOrder: string = JSON.parse(createPurchaseResult.purchaseData).jwsPurchaseOrder; + let purchaseStr = JWTUtil.decodeJwtObj(jwsPurchaseOrder); + let purchaseOrderPayload = JSON.parse(purchaseStr) as PurchaseOrderPayload; + let map: Map = new Map(); + map.set('payment', args) + map.set('transactionIdentifier', purchaseOrderPayload.purchaseOrderId) + map.set('transactionState', TransactionState.purchased) + this.observer?.invokeMethod('updatedTransactions', [map]) + } + result.success(null) + }).catch((err: BusinessError) => { + Log.e(TAG, `Failed to create purchase1. Code is ${err.code}, message is ${err.message}`); + this.errorPurchase(args, err); + }) + } catch (err) { + const e: BusinessError = err as BusinessError; + const msg: string = `Failed to create purchase. Code is ${e.code}, message is ${e.message}`; + Log.e(TAG, msg); + this.errorPurchase(args, err); + } + } + + errorPurchase(args: Map, err: BusinessError) { + let map: Map = new Map(); + let iKError: IKError = new IKError() + if (err) { + iKError.code = err.code + iKError.domain = err.message + } + map.set('error', iKError) + map.set('payment', args) + map.set('transactionState', TransactionState.failed) + this.observer?.invokeMethod('updatedTransactions', [map]) + } + + finishPurchase(productType: iap.ProductType, purchaseToken: string, purchaseOrderId: string, result?: MethodResult) { + let finishPurchaseParam: iap.FinishPurchaseParameter = { + productType: productType, + purchaseToken: purchaseToken, + purchaseOrderId: purchaseOrderId + }; + iap.finishPurchase(this.context, finishPurchaseParam).then(() => { + Log.i(TAG, 'Succeeded in finishing purchase.'); + result?.success(true); + }).catch((err: BusinessError) => { + Log.e(TAG, `Failed to finish purchase. Code is ${err.code}, message is ${err.message}`); + result?.error('' + err.code, err.message, err); + }); + } + + queryPurchasesAsync(productType: iap.ProductType, result: MethodResult) { + let param: iap.QueryPurchasesParameter = { + productType: productType, + queryType: iap.PurchaseQueryType.UNFINISHED + }; + iap.queryPurchases(this.context, param).then((res: iap.QueryPurchaseResult) => { + let purchaseDataList: string[] = res.purchaseDataList; + if (purchaseDataList === undefined || purchaseDataList.length <= 0) { + /* 购买成功后, 查询不到购买订单 返回失败. */ + Log.i(TAG, 'queryPurchases, purchaseDataList empty'); + result.error('UNAVAILABLE', 'purchaseDataList empty!', null); + return; + } + this.purchaseCompletionByTypeProcessing(purchaseDataList, result) + }).catch((err: BusinessError) => { + Log.e(TAG, `Failed to query purchases. Code is ${err.code}, message is ${err.message}`); + result.error('' + err.code, err.message, err); + }); + } + + purchaseCompletionByTypeProcessing(purchaseDataList: string[], result: MethodResult) { + for (let i = 0; i < purchaseDataList.length; i++) { + let purchaseData = purchaseDataList[i]; + let type: number = JSON.parse(purchaseData).type + let purchaseToken = '' + let purchaseOrderId = '' + //自动订阅类型 + if (type == iap.ProductType.AUTORENEWABLE) { + let jwsSubscriptionStatus = (JSON.parse(purchaseData) as PurchaseData).jwsSubscriptionStatus; + if (!jwsSubscriptionStatus) { + Log.e(TAG, 'queryPurchases, jwsSubscriptionStatus invalid'); + continue; + } + let purchaseStr = JWTUtil.decodeJwtObj(jwsSubscriptionStatus); + let purchaseOrderPayload = JSON.parse(purchaseStr) as PurchaseSubGroupStatusPayload; + if (purchaseOrderPayload.lastSubscriptionStatus) { + purchaseToken = purchaseOrderPayload.lastSubscriptionStatus.purchaseToken + purchaseOrderId = purchaseOrderPayload.lastSubscriptionStatus.lastPurchaseOrder.purchaseOrderId + } + } else { + //非自动订阅类型 + let jwsPurchaseOrder = (JSON.parse(purchaseData) as PurchaseData).jwsPurchaseOrder; + if (!jwsPurchaseOrder) { + Log.e(TAG, 'queryPurchases, jwsPurchaseOrder invalid'); + continue; + } + let purchaseStr = JWTUtil.decodeJwtObj(jwsPurchaseOrder); + let purchaseOrderPayload = JSON.parse(purchaseStr) as PurchaseOrderPayload; + purchaseToken = purchaseOrderPayload.purchaseToken + purchaseOrderId = purchaseOrderPayload.purchaseOrderId + } + this.finishPurchase(type, purchaseToken, purchaseOrderId, result); + } + } + + iapFinishPurchase(call: MethodCall, result: MethodResult) { + let finishPurchaseMap: Map = call.args as Map + let productIdentifier: string = finishPurchaseMap.get('productIdentifier') as string + let product: iap.Product = this.productDetailsMap.get(productIdentifier) + // let productType: string = finishPurchaseMap.get('productType') as string + let productType: iap.ProductType = product.type + if (!product) { + Log.i(TAG, `Failed to get productType.`); + result.error('UNAVAILABLE', 'productType is null!', null); + return; + } else { + this.queryPurchasesAsync(productType, result); + } + } + + startObserver() { + this.observer = this.methodChannel + } + + stopObserver() { + this.observer = null + } +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/main/ets/components/MethodNames.ets b/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/main/ets/components/MethodNames.ets new file mode 100644 index 0000000000000000000000000000000000000000..1c6e415b1da952b8ed70d0c654b4fc2ab19d91f9 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/main/ets/components/MethodNames.ets @@ -0,0 +1,26 @@ +/* +* Copyright (c) 2024 SwanLink (Jiangsu) Technology Development Co., LTD. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +export class MethodNames { + static QUERY_ENVIRONMENT_STATUS: string = "iap#queryEnvironmentStatus"; + static QUERY_PRODUCTS: string = "iap#queryProducts"; + static CREATE_PURCHASE: string = "iap#createPurchase"; + static START_OBSERVING_TRANSACTION_QUEUE: string = "iap#startObservingTransactionQueue"; + static STOP_OBSERVING_TRANSACTION_QUEUE: string = "iap#stopObservingTransactionQueue"; + static RETRIEVE_RECEIPT_DATA: string = "iap#retrieveReceiptData"; + static TRANSACTIONS: string = "iap#transactions"; + static FINISH_PURCHASE: string = "iap#finishPurchase"; + static RESTORE_TRANSACTIONS: string = "iap#restoreTransactions"; +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/main/ets/components/ProductType.ets b/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/main/ets/components/ProductType.ets new file mode 100644 index 0000000000000000000000000000000000000000..7ca4c1205a2503749f95c40a9f99be343bfc9b62 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/main/ets/components/ProductType.ets @@ -0,0 +1,21 @@ +/* +* Copyright (c) 2024 SwanLink (Jiangsu) Technology Development Co., LTD. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import { iap } from '@kit.IAPKit' +export const ProductType: Array = [ + iap.ProductType.CONSUMABLE, + iap.ProductType.NONCONSUMABLE, + iap.ProductType.AUTORENEWABLE +] \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/main/ets/components/PurchaseData.ets b/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/main/ets/components/PurchaseData.ets new file mode 100644 index 0000000000000000000000000000000000000000..8440c438ec9c4e4c6acaf59754c5c22ffd76d600 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/main/ets/components/PurchaseData.ets @@ -0,0 +1,20 @@ +/* +* Copyright (c) 2024 SwanLink (Jiangsu) Technology Development Co., LTD. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +export interface PurchaseData { + type: number; + jwsPurchaseOrder?: string; + jwsSubscriptionStatus?: string; +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/main/ets/components/PurchaseOrderPayload.ets b/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/main/ets/components/PurchaseOrderPayload.ets new file mode 100644 index 0000000000000000000000000000000000000000..05fa8036fb1af5e633e2ebc0222a85a6354f9696 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/main/ets/components/PurchaseOrderPayload.ets @@ -0,0 +1,50 @@ +/* +* Copyright (c) 2024 SwanLink (Jiangsu) Technology Development Co., LTD. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import { Any } from '@ohos/flutter_ohos'; + +export class PurchaseOrderPayload { + applicationId: string = ''; + countryCode: string = ''; + environment: string = ''; + payOrderId: string = ''; + price: number = 0; + productId: string = ''; + productType: number = 0; + purchaseOrderId: string = ''; + purchaseTime: number = 0; + purchaseToken: string = ''; + signedTime: number = 0; +} + +export class PurchaseSubGroupStatusPayload { + environment: string = ''; + applicationId: string = ''; + packageName: string = ''; + subGroupId: string = ''; + lastSubscriptionStatus?: PurchaseSubscriptionStatus; + historySubscriptionStatusList?: Any[] = []; +} + +export class PurchaseSubscriptionStatus { + subGroupGenerationId: string = ''; + subscriptionId: string = ''; + purchaseToken: string = ''; + status: string = ''; + expiresTime: number = 0; + lastPurchaseOrder?: Any = {}; + recentPurchaseOrderList?: Any[] = []; + renewalInfo?: Any = {}; +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/main/ets/components/TransactionState.ts b/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/main/ets/components/TransactionState.ts new file mode 100644 index 0000000000000000000000000000000000000000..a9a82a173075e7be1d8487bcd0aecb3426ef9387 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/main/ets/components/TransactionState.ts @@ -0,0 +1,23 @@ +/* +* Copyright (c) 2024 SwanLink (Jiangsu) Technology Development Co., LTD. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +export enum TransactionState { + purchasing = 0, + purchased = 1, + failed = 2, + restored = 3, + deferred = 4, + unspecified = -1, +} diff --git a/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/main/module.json5 b/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..098fbca2fca6d6869d1c7c7eec4f00fa8f10663f --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/main/module.json5 @@ -0,0 +1,11 @@ +{ + "module": { + "name": "in_app_purchase_ohos", + "type": "har", + "deviceTypes": [ + "default", + "tablet", + "2in1" + ] + } +} diff --git a/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/main/resources/base/element/string.json b/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..f51a9c8461a55f6312ef950344e3145b7f82d607 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/main/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "page_show", + "value": "page from package" + } + ] +} diff --git a/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/main/resources/en_US/element/string.json b/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/main/resources/en_US/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..f51a9c8461a55f6312ef950344e3145b7f82d607 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/main/resources/en_US/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "page_show", + "value": "page from package" + } + ] +} diff --git a/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/main/resources/zh_CN/element/string.json b/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/main/resources/zh_CN/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..f51a9c8461a55f6312ef950344e3145b7f82d607 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "page_show", + "value": "page from package" + } + ] +} diff --git a/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/ohosTest/ets/test/Ability.test.ets b/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/ohosTest/ets/test/Ability.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..40eab016e599b3fde160825d05015e9388598f93 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/ohosTest/ets/test/Ability.test.ets @@ -0,0 +1,50 @@ +/* +* Copyright (c) 2024 SwanLink (Jiangsu) Technology Development Co., LTD. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'; + +export default function abilityTest() { + describe('ActsAbilityTest', () => { + // Defines a test suite. Two parameters are supported: test suite name and test suite function. + beforeAll(() => { + // Presets an action, which is performed only once before all test cases of the test suite start. + // This API supports only one parameter: preset action function. + }) + beforeEach(() => { + // Presets an action, which is performed before each unit test case starts. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: preset action function. + }) + afterEach(() => { + // Presets a clear action, which is performed after each unit test case ends. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: clear action function. + }) + afterAll(() => { + // Presets a clear action, which is performed after all test cases of the test suite end. + // This API supports only one parameter: clear action function. + }) + it('assertContain', 0, () => { + // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. + hilog.info(0x0000, 'testTag', '%{public}s', 'it begin'); + let a = 'abc'; + let b = 'b'; + // Defines a variety of assertion methods, which are used to declare expected boolean conditions. + expect(a).assertContain(b); + expect(a).assertEqual(a); + }) + }) +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/ohosTest/ets/test/List.test.ets b/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/ohosTest/ets/test/List.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..a88ef7e80505428a44812fceb130b4f31d51960d --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/ohosTest/ets/test/List.test.ets @@ -0,0 +1,20 @@ +/* +* Copyright (c) 2024 SwanLink (Jiangsu) Technology Development Co., LTD. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import abilityTest from './Ability.test'; + +export default function testsuite() { + abilityTest(); +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/ohosTest/module.json5 b/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/ohosTest/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..7b2341f33368780796dabc95e347a14bc5d0f7d8 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/ohosTest/module.json5 @@ -0,0 +1,13 @@ +{ + "module": { + "name": "in_app_purchase_test", + "type": "feature", + "deviceTypes": [ + "default", + "tablet", + "2in1" + ], + "deliveryWithInstall": true, + "installationFree": false + } +} diff --git a/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/test/List.test.ets b/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/test/List.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..e3f1904373eb8182a07290c5b9a1287d1a9dd219 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/test/List.test.ets @@ -0,0 +1,20 @@ +/* +* Copyright (c) 2024 SwanLink (Jiangsu) Technology Development Co., LTD. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import localUnitTest from './LocalUnit.test'; + +export default function testsuite() { + localUnitTest(); +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/test/LocalUnit.test.ets b/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/test/LocalUnit.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..13b2128b8428b9368a257451baf665e38434bbac --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/ohos/src/test/LocalUnit.test.ets @@ -0,0 +1,48 @@ +/* +* Copyright (c) 2024 SwanLink (Jiangsu) Technology Development Co., LTD. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'; + +export default function localUnitTest() { + describe('localUnitTest', () => { + // Defines a test suite. Two parameters are supported: test suite name and test suite function. + beforeAll(() => { + // Presets an action, which is performed only once before all test cases of the test suite start. + // This API supports only one parameter: preset action function. + }); + beforeEach(() => { + // Presets an action, which is performed before each unit test case starts. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: preset action function. + }); + afterEach(() => { + // Presets a clear action, which is performed after each unit test case ends. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: clear action function. + }); + afterAll(() => { + // Presets a clear action, which is performed after all test cases of the test suite end. + // This API supports only one parameter: clear action function. + }); + it('assertContain', 0, () => { + // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. + let a = 'abc'; + let b = 'b'; + // Defines a variety of assertion methods, which are used to declare expected boolean conditions. + expect(a).assertContain(b); + expect(a).assertEqual(a); + }); + }); +} \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_ohos/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_ohos/pubspec.yaml new file mode 100644 index 0000000000000000000000000000000000000000..1eef2cb8b149e712ddef32f9e1a0dd9aa2dbb62b --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_ohos/pubspec.yaml @@ -0,0 +1,35 @@ +name: in_app_purchase_ohos +description: An implementation for the Ohos platform of the Flutter `in_app_purchase` plugin. This uses the ohoa IAPKit APIs. +repository: https://gitee.com/openharmony-sig/flutter_packages/tree/master/packages/in_app_purchase/in_app_purchase_ohos +issue_tracker: https://gitee.com/openharmony-sig/flutter_packages/issues +version: 0.1.0 + +environment: + sdk: ">=2.19.0 <4.0.0" + flutter: ">=3.7.0" + +flutter: + plugin: + implements: in_app_purchase + platforms: + ohos: + pluginClass: InAppPurchasePlugin + +dependencies: + collection: ^1.15.0 + flutter: + sdk: flutter + in_app_purchase_platform_interface: ^1.3.0 + json_annotation: ^4.8.0 + +dev_dependencies: + build_runner: ^2.0.0 + flutter_test: + sdk: flutter + json_serializable: ^6.3.1 + mockito: 5.4.0 + test: ^1.16.0 + +topics: + - in-app-purchase + - payment