Toggle navigation
Toggle navigation
This project
Loading...
Sign in
OnePoem
/
OnePoem-App
Go to a project
Toggle navigation
Toggle navigation pinning
Projects
Groups
Snippets
Help
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Snippets
Network
Create a new issue
Builds
Commits
Issue Boards
Authored by
reason
2022-06-08 19:05:31 +0800
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
4254da3b329e74686694597973628cd4b6675dc8
4254da3b
1 parent
8fdf58d0
增加支付
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
264 additions
and
83 deletions
lib/main.dart
lib/membership/page/membership_page.dart
lib/payment/payment_service.dart
lib/main.dart
View file @
4254da3
...
...
@@ -95,7 +95,7 @@ class MyApp extends StatelessWidget {
}
interceptors
.
add
(
AdapterInterceptor
());
configDio
(
baseUrl:
'https://
www.yiyan.pub
/api/v1/'
,
baseUrl:
'https://
api.parlando.ink
/api/v1/'
,
interceptors:
interceptors
,
);
}
...
...
lib/membership/page/membership_page.dart
View file @
4254da3
import
'dart:async'
;
import
'dart:io'
;
import
'dart:ui'
;
import
'package:Parlando/apis/api_response.dart'
;
import
'package:Parlando/login/login_router.dart'
;
import
'package:Parlando/membership/models/membership_entity.dart'
;
import
'package:Parlando/membership/view_models/membership_view_model.dart'
;
import
'package:Parlando/payment/payment_service.dart'
;
import
'package:Parlando/res/constant.dart'
;
import
'package:cached_network_image/cached_network_image.dart'
;
import
'package:flustars/flustars.dart'
;
...
...
@@ -14,6 +14,7 @@ import 'package:Parlando/res/resources.dart';
import
'package:Parlando/routers/fluro_navigator.dart'
;
import
'package:Parlando/extension/int_extension.dart'
;
import
'package:flutter_inapp_purchase/flutter_inapp_purchase.dart'
;
import
'package:getwidget/getwidget.dart'
;
import
'package:provider/provider.dart'
;
class
MembershipPage
extends
StatefulWidget
{
...
...
@@ -27,18 +28,7 @@ class MembershipPageState extends State<MembershipPage>
with
WidgetsBindingObserver
{
bool
_isLoading
=
false
;
late
StreamSubscription
_purchaseUpdatedSubscription
;
late
StreamSubscription
_purchaseErrorSubscription
;
late
StreamSubscription
_conectionSubscription
;
final
List
<
String
>
_productLists
=
Platform
.
isAndroid
?
[
'test.yiyan.vip.1.month'
,
]
:
[
'test.yiyan.vip.1.month'
];
String
_platformVersion
=
'Unknown'
;
List
<
IAPItem
>
_items
=
[];
List
<
PurchasedItem
>
_purchases
=
[];
final
List
<
Widget
>
_productWidgets
=
<
Widget
>[];
@override
void
initState
()
{
...
...
@@ -48,8 +38,7 @@ class MembershipPageState extends State<MembershipPage>
.
setSelectedMembership
(
null
);
Provider
.
of
<
MembershipViewProvider
>(
context
,
listen:
false
)
.
fetchMembershipData
(
'0'
);
initPlatformState
();
PaymentService
.
instance
.
initConnection
();
}
else
{
NavigatorUtils
.
push
(
context
,
LoginRouter
.
loginPage
,
replace:
true
);
}
...
...
@@ -130,9 +119,11 @@ class MembershipPageState extends State<MembershipPage>
SizedBox
(
width:
double
.
infinity
,
height:
100
,
child:
Column
(
children:
_renderInApps
(),
),
child:
_productWidgets
.
isEmpty
?
Column
(
children:
_productWidgets
,
)
:
const
GFLoader
(),
),
Gaps
.
vGap24
,
Text
(
...
...
@@ -145,7 +136,7 @@ class MembershipPageState extends State<MembershipPage>
Gaps
.
vGap10
,
Row
(
mainAxisAlignment:
MainAxisAlignment
.
spaceBetween
,
MainAxisAlignment
.
spaceBetween
,
mainAxisSize:
MainAxisSize
.
min
,
crossAxisAlignment:
CrossAxisAlignment
.
center
,
children:
[
...
...
@@ -220,72 +211,15 @@ class MembershipPageState extends State<MembershipPage>
@override
void
dispose
()
{
_conectionSubscription
.
cancel
();
PaymentService
.
instance
.
dispose
();
super
.
dispose
();
}
Future
<
void
>
initPlatformState
()
async
{
String
platformVersion
;
// Platform messages may fail, so we use a try/catch PlatformException.
// prepare
var
result
=
await
FlutterInappPurchase
.
instance
.
initialize
();
print
(
'result:
$result
'
);
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if
(!
mounted
)
return
;
setState
(()
{
// _platformVersion = platformVersion;
});
// refresh items for android
try
{
String
msg
=
await
FlutterInappPurchase
.
instance
.
consumeAll
();
print
(
'consumeAllItems:
$msg
'
);
}
catch
(
err
)
{
print
(
'consumeAllItems error:
$err
'
);
}
_conectionSubscription
=
FlutterInappPurchase
.
connectionUpdated
.
listen
((
connected
)
{
print
(
'connected:
$connected
'
);
});
_purchaseUpdatedSubscription
=
FlutterInappPurchase
.
purchaseUpdated
.
listen
((
productItem
)
{
print
(
'purchase-updated:
$productItem
'
);
});
_purchaseErrorSubscription
=
FlutterInappPurchase
.
purchaseError
.
listen
((
purchaseError
)
{
print
(
'purchase-error:
$purchaseError
'
);
});
List
<
IAPItem
>
items
=
await
FlutterInappPurchase
.
instance
.
getSubscriptions
(
_productLists
);
for
(
var
item
in
items
)
{
print
(
'
${item.toString()}
'
);
_items
.
add
(
item
);
}
setState
(()
{
_items
=
items
;
_purchases
=
[];
});
}
List
<
Widget
>
_renderInApps
()
{
List
<
Widget
>
widgets
=
<
Widget
>[];
for
(
IAPItem
item
in
_items
)
{
widgets
.
add
(
Text
(
item
.
title
!));
void
_renderInApps
()
async
{
List
<
IAPItem
>
items
=
await
PaymentService
.
instance
.
products
;
for
(
IAPItem
item
in
items
)
{
_productWidgets
.
add
(
Text
(
item
.
title
!));
}
return
widgets
;
}
void
_requestPurchase
(
IAPItem
item
)
{
FlutterInappPurchase
.
instance
.
requestPurchase
(
item
.
productId
!);
setState
(()
{});
}
}
...
...
lib/payment/payment_service.dart
0 → 100644
View file @
4254da3
import
'dart:async'
;
import
'dart:convert'
;
import
'dart:io'
;
import
'package:Parlando/util/toast_utils.dart'
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter_inapp_purchase/flutter_inapp_purchase.dart'
;
class
PaymentService
{
/// We want singleton object of ``PaymentService`` so create private constructor
///
/// Use PaymentService as ``PaymentService.instance``
PaymentService
.
_internal
();
static
final
PaymentService
instance
=
PaymentService
.
_internal
();
/// To listen the status of connection between app and the billing server
late
StreamSubscription
<
ConnectionResult
>
_connectionSubscription
;
/// To listen the status of the purchase made inside or outside of the app (App Store / Play Store)
///
/// If status is not error then app will be notied by this stream
late
StreamSubscription
<
PurchasedItem
?>
_purchaseUpdatedSubscription
;
/// To listen the errors of the purchase
late
StreamSubscription
<
PurchaseResult
?>
_purchaseErrorSubscription
;
/// List of product ids you want to fetch
final
List
<
String
>
_productIds
=
[
'test.yiyan.vip.1.month'
];
/// All available products will be store in this list
late
List
<
IAPItem
>
_products
;
/// All past purchases will be store in this list
late
List
<
PurchasedItem
>
_pastPurchases
;
/// view of the app will subscribe to this to get notified
/// when premium status of the user changes
final
ObserverList
<
Function
>
_proStatusChangedListeners
=
ObserverList
<
Function
>();
/// view of the app will subscribe to this to get errors of the purchase
final
ObserverList
<
Function
(
String
)>
_errorListeners
=
ObserverList
<
Function
(
String
)>();
/// logged in user's premium status
bool
_isProUser
=
false
;
bool
get
isProUser
=>
_isProUser
;
/// view can subscribe to _proStatusChangedListeners using this method
addToProStatusChangedListeners
(
Function
callback
)
{
_proStatusChangedListeners
.
add
(
callback
);
}
/// view can cancel to _proStatusChangedListeners using this method
removeFromProStatusChangedListeners
(
Function
callback
)
{
_proStatusChangedListeners
.
remove
(
callback
);
}
/// view can subscribe to _errorListeners using this method
addToErrorListeners
(
Function
(
String
)
callback
)
{
_errorListeners
.
add
(
callback
);
}
/// view can cancel to _errorListeners using this method
removeFromErrorListeners
(
Function
(
String
)
callback
)
{
_errorListeners
.
remove
(
callback
);
}
/// Call this method to notify all the subsctibers of _proStatusChangedListeners
void
_callProStatusChangedListeners
()
{
for
(
var
callback
in
_proStatusChangedListeners
)
{
callback
();
}
}
/// Call this method to notify all the subsctibers of _errorListeners
void
_callErrorListeners
(
String
error
)
{
for
(
var
callback
in
_errorListeners
)
{
callback
(
error
);
}
}
/// Call this method at the startup of you app to initialize connection
/// with billing server and get all the necessary data
void
initConnection
()
{
var
result
=
FlutterInappPurchase
.
instance
.
initialize
();
print
(
"___________________________"
);
print
(
"result:
$result
"
);
_connectionSubscription
=
FlutterInappPurchase
.
connectionUpdated
.
listen
((
connected
)
{});
_purchaseUpdatedSubscription
=
FlutterInappPurchase
.
purchaseUpdated
.
listen
(
_handlePurchaseUpdate
);
_purchaseErrorSubscription
=
FlutterInappPurchase
.
purchaseError
.
listen
(
_handlePurchaseError
);
_getItems
();
_getPastPurchases
();
}
/// call when user close the app
void
dispose
()
{
_connectionSubscription
.
cancel
();
_purchaseErrorSubscription
.
cancel
();
_purchaseUpdatedSubscription
.
cancel
();
FlutterInappPurchase
.
instance
.
finalize
();
}
void
_handlePurchaseError
(
PurchaseResult
?
purchaseError
)
{
_callErrorListeners
(
purchaseError
!.
message
.
toString
());
}
/// Called when new updates arrives at ``purchaseUpdated`` stream
void
_handlePurchaseUpdate
(
PurchasedItem
?
productItem
)
async
{
if
(
Platform
.
isAndroid
)
{
await
_handlePurchaseUpdateAndroid
(
productItem
!);
}
else
{
await
_handlePurchaseUpdateIOS
(
productItem
!);
}
}
Future
<
void
>
_handlePurchaseUpdateIOS
(
PurchasedItem
purchasedItem
)
async
{
switch
(
purchasedItem
.
transactionStateIOS
)
{
case
TransactionState
.
deferred
:
// Edit: This was a bug that was pointed out here : https://github.com/dooboolab/flutter_inapp_purchase/issues/234
// FlutterInappPurchase.instance.finishTransaction(purchasedItem);
break
;
case
TransactionState
.
failed
:
_callErrorListeners
(
"Transaction Failed"
);
FlutterInappPurchase
.
instance
.
finishTransaction
(
purchasedItem
);
break
;
case
TransactionState
.
purchased
:
await
_verifyAndFinishTransaction
(
purchasedItem
);
break
;
case
TransactionState
.
purchasing
:
break
;
case
TransactionState
.
restored
:
FlutterInappPurchase
.
instance
.
finishTransaction
(
purchasedItem
);
break
;
default
:
}
}
/// three purchase state https://developer.android.com/reference/com/android/billingclient/api/Purchase.PurchaseState
/// 0 : UNSPECIFIED_STATE
/// 1 : PURCHASED
/// 2 : PENDING
Future
<
void
>
_handlePurchaseUpdateAndroid
(
PurchasedItem
purchasedItem
)
async
{
switch
(
purchasedItem
.
purchaseStateAndroid
)
{
case
PurchaseState
.
purchased
:
if
(
purchasedItem
.
isAcknowledgedAndroid
==
null
)
{
await
_verifyAndFinishTransaction
(
purchasedItem
);
}
break
;
default
:
_callErrorListeners
(
"Something went wrong"
);
}
}
/// Call this method when status of purchase is success
/// Call API of your back end to verify the receipt
/// back end has to call billing server's API to verify the purchase token
_verifyAndFinishTransaction
(
PurchasedItem
purchasedItem
)
async
{
bool
isValid
=
false
;
try
{
// Call API
isValid
=
await
_verifyPurchase
(
purchasedItem
);
}
on
Exception
{
_callErrorListeners
(
"暂时无法购买,请稍后再试!"
);
return
;
}
if
(
isValid
)
{
FlutterInappPurchase
.
instance
.
finishTransaction
(
purchasedItem
);
_isProUser
=
true
;
// save in sharedPreference here
_callProStatusChangedListeners
();
}
else
{
_callErrorListeners
(
"Verification failed"
);
}
}
Future
<
List
<
IAPItem
>>
get
products
async
{
if
(
_products
==
null
)
{
await
_getItems
();
}
return
_products
;
}
Future
<
void
>
_getItems
()
async
{
List
<
IAPItem
>
items
=
await
FlutterInappPurchase
.
instance
.
getSubscriptions
(
_productIds
);
_products
=
[];
for
(
var
item
in
items
)
{
_products
.
add
(
item
);
}
print
(
"############"
);
print
(
_products
);
}
void
_getPastPurchases
()
async
{
// remove this if you want to restore past purchases in iOS
if
(
Platform
.
isIOS
)
{
return
;
}
List
<
PurchasedItem
>?
purchasedItems
=
await
FlutterInappPurchase
.
instance
.
getAvailablePurchases
();
for
(
var
purchasedItem
in
purchasedItems
!)
{
bool
isValid
=
false
;
if
(
Platform
.
isAndroid
)
{
Map
map
=
json
.
decode
(
purchasedItem
.
transactionReceipt
!);
// if your app missed finishTransaction due to network or crash issue
// finish transactins
if
(!
map
[
'acknowledged'
])
{
isValid
=
await
_verifyPurchase
(
purchasedItem
);
if
(
isValid
)
{
FlutterInappPurchase
.
instance
.
finishTransaction
(
purchasedItem
);
_isProUser
=
true
;
_callProStatusChangedListeners
();
}
}
else
{
_isProUser
=
true
;
_callProStatusChangedListeners
();
}
}
}
_pastPurchases
=
[];
_pastPurchases
.
addAll
(
purchasedItems
);
}
Future
<
void
>
buyProduct
(
IAPItem
item
)
async
{
try
{
await
FlutterInappPurchase
.
instance
.
requestSubscription
(
item
.
productId
.
toString
());
}
catch
(
error
)
{
Toast
.
show
(
"购买失败!"
);
}
}
_verifyPurchase
(
PurchasedItem
purchasedItem
)
{}
}
Please
register
or
login
to post a comment