(一)、内购商品类型与配置
具体过程参考下方参考资料链接,基本上是手把手的教你配置了。
注意点
1、在截图的尺寸,我给的是1242*2208,试了其他几个尺寸都失败了。
2、创建IAP想好类型和productID,因为不可更改,建议:com.xxx.xxx的形式
3、所有商品配置完好显示得是: 提交状态
image.png
image.png
(二)、银行卡与税务信息配置
付费协议和免费协议 的状态需要 标识:有效
image.png
(三)、IAP 源码
1、集成插件:
使用到的第三方插件: in_app_purchase
插件官网地址:https://pub.dev/packages/in_app_purchase
将插件添加至yaml文件,然后执行flutter pub get
2、支付核心逻辑代码
import 'dart:async';
import 'dart:io';
import 'package:in_app_purchase/in_app_purchase.dart';
import 'package:in_app_purchase_storekit/in_app_purchase_storekit.dart';
import 'package:in_app_purchase_android/in_app_purchase_android.dart';
class BuyEngin {
late StreamSubscription<List<PurchaseDetails>> _subscription;
late InAppPurchase _inAppPurchase;
List<ProductDetails> _products = []; //内购的商品对象集合
//初始化购买组件
void initializeInAppPurchase() {
// 初始化in_app_purchase插件
_inAppPurchase = InAppPurchase.instance;
//监听购买的事件
final Stream<List<PurchaseDetails>> purchaseUpdated =
_inAppPurchase.purchaseStream;
_subscription = purchaseUpdated.listen((purchaseDetailsList) {
_listenToPurchaseUpdated(purchaseDetailsList);
}, onDone: () {
_subscription.cancel();
}, onError: (error) {
error.printError();
print("购买失败了");
});
}
void resumePurchase() {
_inAppPurchase.restorePurchases();
}
/// 加载全部的商品
void buyProduct(String productId) async {
print("请求商品id $productId");
List<String> outProducts = [productId];
final bool available = await _inAppPurchase.isAvailable();
if (!available) {
// ToastUtil.showToast("无法连接到商店");
print("无法连接到商店");
return;
}
//开始购买
// ToastUtil.showToast("连接成功-开始查询全部商品");
print("连接成功-开始查询全部商品");
List<String> kIds = outProducts;
final ProductDetailsResponse response =
await _inAppPurchase.queryProductDetails(kIds.toSet());
print("商品获取结果 ${response.productDetails}");
if (response.notFoundIDs.isNotEmpty) {
// ToastUtil.showToast("无法找到指定的商品");
print("无法找到指定的商品");
// ToastUtil.showToast("无法找到指定的商品 数量 " + response.productDetails.length.toString());
return;
}
// 处理查询到的商品列表
List<ProductDetails> products = response.productDetails;
print("products ==== ${products.length}");
if (products.isNotEmpty) {
//赋值内购商品集合
_products = products;
}
print("全部商品加载完成了,可以启动购买了,总共商品数量为:${products.length}");
//先恢复可重复购买
// await _inAppPurchase. ();
startPurchase(productId);
}
// 调用此函数以启动购买过程
void startPurchase(String productId) async {
print("购买的商品id为$productId");
if (_products.isNotEmpty) {
// ToastUtil.showToast("准备开始启动购买流程");
try {
ProductDetails productDetails = _getProduct(productId);
print(
"一切正常,开始购买,信息如下:title: ${productDetails.title} desc:${productDetails.description} "
"price:${productDetails.price} currencyCode:${productDetails.currencyCode} currencySymbol:${productDetails.currencySymbol}");
_inAppPurchase.buyConsumable(
purchaseParam: PurchaseParam(productDetails: productDetails));
} catch (e) {
print(e.toString());
print("购买失败了");
}
} else {
print("当前没有商品无法调用购买逻辑");
}
}
// 根据产品ID获取产品信息
ProductDetails _getProduct(String productId) {
return _products.firstWhere((product) => product.id == productId);
}
/// 内购的购买更新监听
void _listenToPurchaseUpdated(
List<PurchaseDetails> purchaseDetailsList) async {
for (PurchaseDetails purchase in purchaseDetailsList) {
if (purchase.status == PurchaseStatus.pending) {
// 等待支付完成
_handlePending();
} else if (purchase.status == PurchaseStatus.canceled) {
// 取消支付
_handleCancel(purchase);
} else if (purchase.status == PurchaseStatus.error) {
// 购买失败
_handleError(purchase.error!);
} else if (purchase.status == PurchaseStatus.purchased ||
purchase.status == PurchaseStatus.restored) {
// ToastUtil.showToast(DataConfig.getShowName("Pay_Success_Tip"));
//完成购买, 到服务器验证
if (Platform.isAndroid) {
var googleDetail = purchase as GooglePlayPurchaseDetails;
checkAndroidPayInfo(googleDetail);
} else if (Platform.isIOS) {
var appstoreDetail = purchase as AppStorePurchaseDetails;
checkApplePayInfo(appstoreDetail);
}
}
}
}
/// 购买失败
void _handleError(IAPError iapError) {
// ToastUtil.showToast("${DataConfig.getShowName("Purchase_Failed")}:${iapError?.code} message${iapError?.message}");
}
/// 等待支付
void _handlePending() {
print("等待支付");
}
/// 取消支付
void _handleCancel(PurchaseDetails purchase) {
_inAppPurchase.completePurchase(purchase);
}
/// Android支付成功的校验
void checkAndroidPayInfo(GooglePlayPurchaseDetails googleDetail) async {
_inAppPurchase.completePurchase(googleDetail);
print("安卓支付交易ID为${googleDetail.purchaseID}");
print("安卓支付验证收据为${googleDetail.verificationData.serverVerificationData}");
}
/// Apple支付成功的校验
void checkApplePayInfo(AppStorePurchaseDetails appstoreDetail) async {
_inAppPurchase.completePurchase(appstoreDetail);
print("Apple支付交易ID为${appstoreDetail.purchaseID}");
print(
"Apple支付验证收据为${appstoreDetail.verificationData.serverVerificationData}");
}
void onClose() {
if (Platform.isIOS) {
final InAppPurchaseStoreKitPlatformAddition iosPlatformAddition =
_inAppPurchase
.getPlatformAddition<InAppPurchaseStoreKitPlatformAddition>();
iosPlatformAddition.setDelegate(null);
}
_subscription.cancel();
}
}
3、调用代码
BuyEngin buyEngin = BuyEngin();
buyEngin.initializeInAppPurchase();
buyEngin.buyProduct("45566");// 你的IAP 的productID
4、点击执行结果打印log
请求商品id com.xxx.001
连接成功-开始查询全部商品
商品获取结果 [Instance of 'AppStoreProductDetails']
products ==== 1
全部商品加载完成了,可以启动购买了,总共商品数量为:1
购买的商品id为 com.xxx.001
一切正常,开始购买,信息如下:title: 100金币 desc:¥24.00 price:¥24 currencyCode:CNY currencySymbol:¥
等待支付
(四)遇到的问题 response.products为空
原因:
- 您需要更新一下,付费App --> 协议 。必须都处于 “有效”状态。(这个很重要)
- 您所新健的 内购项目,必须所有都处于 准备提交状态。 如果有一个是 数据元丢失,代理方法返回都是空的。(这点很重要!!!!)
参考资料:
【1】苹果内购(IAP)从入门到精通(1)-内购商品类型与配置
【2】苹果内购(IAP)从入门到精通(2)-银行卡与税务信息配置