【Flutter】App内购支付集成 Google和Apple支付和服务器验证全流程 ...

打印 上一主题 下一主题

主题 507|帖子 507|积分 1521

Flutter支付集成

前言:

以谷歌内购为例,我们须要做的统共为三步

  • 须要在谷歌市场设置商品,设置测试渠道,设置开辟者账号,设置对应权限。
  • 设置完商品之后,如安在 Flutter 中获取到商品,购买指定商品,消耗商品等。
  • 购买乐成之后,如何到服务器校验是否支付乐成,后台服务器如何设置通行权限,谷歌市场与谷歌云的关联以及相关校验。
购买交易的生命周期
下面是一次性购买或订阅的典型购买流程:

  • 向用户展示他们可以购买什么。
  • 启动购买流程,以便用户担当购买交易。
  • 在您的服务器上验证购买交易。
  • 向用户提供内容。
  • 确认内容已传送给用户。对于消耗型商品,用户要先消耗掉已购商品,才能再次购买。
订阅会主动续订,直到被取消。订阅可处于下面这几种状态:


  • 有效:用户信誉良好,可享用订阅内容。
  • 已取消:用户已取消订阅,但在到期前仍可享用订阅内容。
  • 处于脱期期:用户碰到了付款问题,但仍可享用订阅内容,同时 Google 会重新尝试通过相应的付款方式扣款。
  • 暂时保存:用户碰到了付款问题,不能再享用订阅内容,同时 Google 会重新尝试通过相应的付款方式扣款。
  • 已暂停:用户暂停了其订阅,在恢复之前不能享用订阅内容。
  • 已到期:用户已取消且不能再享用订阅内容。用户在订阅到期时会被视为流失。
支付流程示意图

一 、google开辟者平台设置

首先辈入谷歌开辟者平台
https://developers.google.com/?hl=zh-cn
进入开辟者平台之后,点击google play,创建我们的APP

点击登录管理中心

创建完我们的APP之后,就可以开始设置支付的功能。须要注意的是,在进行谷歌支付测试的时候,须要先提交一个封闭测试版本及以上等级(比方公开版本)的包,然后才可以去创建应用内支付的商品,等这个包提交考核通过之后才可以开始进行谷歌支付的测试。
1.1、创建订价模板

在设置页面

找到付款概况之后,假如没有付款账号,我们填写一些信息,姓名,邮箱,账号,等等信息,创建完成之后我们就可以设置订价的模板。
假如能创建模板分析你付款账号没问题,订价模板是非必须的,无关紧要,但是定义了模板之后会更加方便,到时候创建商品可以直接关联模板,账号下的每一个子应用的内购商品都能关联对应的模板,有一个统一的订价。
如何创建订价模板如下:

我们创建模板之后,就可以定义模板的价格与标题,选择的金额会有对应的汇率转换,比如我创建的新加坡币,假如用港元支付的话,会根据汇率转换为对应的港元支付。

创建完成之后,我们就能看到对应的订价模板如下图所示:

1.2、上架封闭测试App

点击创建轨道

点击创建新的发布版本

署名选择Google管理署名,然后上传aab格式的release版本的包,aab版本的包在这里生成
点开Build,选择Generate Signed Bundle/APK

然后选择app bundle

然后一路next,最后选择release版本,然后finish

然后在输出控制台的build选项卡,即可找到刚刚打出来的aab包

然后上传就可以了。
1.3、创建应用内购商品

此时就可以设置应用内商品了,点击这里进行添加设置:

添加完成后记得激活,否则即使考核通过之后测试的时候也获取不到该商品
点击这里激活商品

这个时候商品的设置就完成了。
接下来添加测试账户,进入封闭测试页面,切换到【测试用户选项卡】,然后创建测试群组,在群组里添加测试职员账户即可

当你的APP考核通过之后,这个页面下方的测试职员参与方式便会见效,如下所示:

就可以将这些链接发给测试职员,让他们去安装进行测试购买。
最后修改一下测试政策状态

选中测试群组,然后将政策状态改为LICENSED

OK,设置完成
二 、Apple开辟者平台添加内购商品

首先使用苹果开辟者账户登录苹果开辟者平台
  https://developer.apple.com/account
点击【App】

添加新的苹果内购商品

添加的时候页面的指引很清晰,就不赘述了,苹果添加内购商品比力简单,加完就可以了。
然后去创建沙盒账户用来做苹果支付测试,回到首页,点击【用户和访问】

点击沙盒,然后添加一个苹果测试账户,这个账户可以是个假的邮箱,不须要是正式的Apple id,比如你可以设置为8888888@qq.com类似之类的账户

添加完点击创建即可

OK,设置完成
三、flutter 代码集成

使用到的官方推出的应用内购插件:
  1. in_app_purchase: ^3.2.0
复制代码
插件官网地址:https://pub.dev/packages/in_app_purchase
使用起来并不复杂,可以说是 Android 与 iOS 的逻辑是一样样的。
将插件添加至yaml文件,然后执行flutter pub get

执行完了记得去IOS和安卓端分别执行pod install 和 gradle sync同步一下第三方插件
然后在项目中新建dart文件,定名为:BuyEngine.dart
然后将以下代码放入:
  1. import 'dart:async';
  2. import 'dart:io';
  3. import 'package:in_app_purchase/in_app_purchase.dart';
  4. import 'package:in_app_purchase_storekit/in_app_purchase_storekit.dart';
  5. import 'package:in_app_purchase_android/in_app_purchase_android.dart';
  6. class BuyEngin{
  7.   StreamSubscription<List<PurchaseDetails>> _subscription;
  8.   InAppPurchase _inAppPurchase;
  9.   List<ProductDetails> _products; //内购的商品对象集合
  10.   //初始化购买组件
  11.   void initializeInAppPurchase() {
  12.     // 初始化in_app_purchase插件
  13.     _inAppPurchase = InAppPurchase.instance;
  14.     //监听购买的事件
  15.     final Stream<List<PurchaseDetails>> purchaseUpdated = _inAppPurchase.purchaseStream;
  16.     _subscription = purchaseUpdated.listen((purchaseDetailsList) {
  17.       _listenToPurchaseUpdated(purchaseDetailsList);
  18.     }, onDone: () {
  19.       _subscription.cancel();
  20.     }, onError: (error) {
  21.       error.printError();
  22.       print("购买失败了");
  23.     });
  24.   }
  25.   void resumePurchase(){
  26.     _inAppPurchase.restorePurchases();
  27.   }
  28.   /// 加载全部的商品
  29.   void buyProduct(String productId) async {
  30.     print("请求商品id " + productId);
  31.     List<String> _outProducts = [productId];
  32.     final bool available = await _inAppPurchase.isAvailable();
  33.     if (!available) {
  34.       // ToastUtil.showToast("无法连接到商店");
  35.       print("无法连接到商店");
  36.       return;
  37.     }
  38.     //开始购买
  39.     // ToastUtil.showToast("连接成功-开始查询全部商品");
  40.     print("连接成功-开始查询全部商品");
  41.     List<String> _kIds = _outProducts;
  42.     final ProductDetailsResponse response = await _inAppPurchase.queryProductDetails(_kIds.toSet());
  43.     print("商品获取结果  " + response.productDetails.toString());
  44.     if (response.notFoundIDs.isNotEmpty) {
  45.       // ToastUtil.showToast("无法找到指定的商品");
  46.       print("无法找到指定的商品");
  47.       // ToastUtil.showToast("无法找到指定的商品 数量 " + response.productDetails.length.toString());
  48.       return;
  49.     }
  50.     // 处理查询到的商品列表
  51.     List<ProductDetails> products = response.productDetails;
  52.     print("products ==== " + products.length.toString());
  53.     if (products.isNotEmpty) {
  54.       //赋值内购商品集合
  55.       _products = products;
  56.     }
  57.     print("全部商品加载完成了,可以启动购买了,总共商品数量为:${products.length}");
  58.     //先恢复可重复购买
  59.     // await _inAppPurchase. ();
  60.     startPurchase(productId);
  61.   }
  62.   // 调用此函数以启动购买过程
  63.   void startPurchase(String productId) async {
  64.     print("购买的商品id为" + productId);
  65.     if (_products != null && _products.isNotEmpty) {
  66.       // ToastUtil.showToast("准备开始启动购买流程");
  67.       try {
  68.         ProductDetails productDetails = _getProduct(productId);
  69.         print("一切正常,开始购买,信息如下:title: ${productDetails.title}  desc:${productDetails.description} "
  70.             "price:${productDetails.price}  currencyCode:${productDetails.currencyCode}  currencySymbol:${productDetails.currencySymbol}");
  71.         _inAppPurchase.buyConsumable(purchaseParam: PurchaseParam(productDetails: productDetails));
  72.       } catch (e) {
  73.         e.printError();
  74.         print("购买失败了");
  75.       }
  76.     } else {
  77.       print("当前没有商品无法调用购买逻辑");
  78.     }
  79.   }
  80.   // 根据产品ID获取产品信息
  81.   ProductDetails _getProduct(String productId) {
  82.     return _products.firstWhere((product) => product.id == productId);
  83.   }
  84.   /// 内购的购买更新监听
  85.   void _listenToPurchaseUpdated(List<PurchaseDetails> purchaseDetailsList) async {
  86.     for (PurchaseDetails purchase in purchaseDetailsList) {
  87.       if (purchase.status == PurchaseStatus.pending) {
  88.         // 等待支付完成
  89.         _handlePending();
  90.       } else if (purchase.status == PurchaseStatus.canceled) {
  91.         // 取消支付
  92.         _handleCancel(purchase);
  93.       } else if (purchase.status == PurchaseStatus.error) {
  94.         // 购买失败
  95.         _handleError(purchase.error);
  96.       } else if (purchase.status == PurchaseStatus.purchased || purchase.status == PurchaseStatus.restored) {
  97.         // ToastUtil.showToast(DataConfig.getShowName("Pay_Success_Tip"));
  98.         //完成购买, 到服务器验证
  99.         if (Platform.isAndroid) {
  100.           var googleDetail = purchase as GooglePlayPurchaseDetails;
  101.           checkAndroidPayInfo(googleDetail);
  102.         } else if (Platform.isIOS) {
  103.           var appstoreDetail = purchase as AppStorePurchaseDetails;
  104.           checkApplePayInfo(appstoreDetail);
  105.         }
  106.       }
  107.     }
  108.   }
  109.   /// 购买失败
  110.   void _handleError(IAPError iapError) {
  111.     // ToastUtil.showToast("${DataConfig.getShowName("Purchase_Failed")}:${iapError?.code} message${iapError?.message}");
  112.   }
  113.   /// 等待支付
  114.   void _handlePending() {
  115.     print("等待支付");
  116.   }
  117.   /// 取消支付
  118.   void _handleCancel(PurchaseDetails purchase) {
  119.     _inAppPurchase.completePurchase(purchase);
  120.   }
  121.   /// Android支付成功的校验
  122.   void checkAndroidPayInfo(GooglePlayPurchaseDetails googleDetail) async {
  123.     _inAppPurchase.completePurchase(googleDetail);
  124.     print("安卓支付交易ID为" + googleDetail.purchaseID);
  125.     print("安卓支付验证收据为" + googleDetail.verificationData.serverVerificationData);
  126.   }
  127.   /// Apple支付成功的校验
  128.   void  checkApplePayInfo(AppStorePurchaseDetails appstoreDetail) async {
  129.     _inAppPurchase.completePurchase(appstoreDetail);
  130.     print("Apple支付交易ID为" + appstoreDetail.purchaseID);
  131.     print("Apple支付验证收据为" + appstoreDetail.verificationData.serverVerificationData);
  132.   }
  133.   @override
  134.   void onClose() {
  135.     if (Platform.isIOS) {
  136.       final InAppPurchaseStoreKitPlatformAddition iosPlatformAddition =
  137.       _inAppPurchase.getPlatformAddition<InAppPurchaseStoreKitPlatformAddition>();
  138.       iosPlatformAddition.setDelegate(null);
  139.     }
  140.     _subscription.cancel();
  141.   }
  142. }
复制代码
至此集成完毕,开始测试谷歌支付
三 、支付测试

在调用支付的地方提前初始化购买插件:
  1. BuyEngin _buyEngin = BuyEngin();
  2. _buyEngin.initializeInAppPurchase();
复制代码
然后调用即可:
  1. _buyEngin.buyProduct("应用内商品ID");
复制代码
应用内商品ID就是你在google开辟者中心或APP Store Connect 设置的应用内购买商品的product ID
假如统统正常,则会正常唤醒谷歌或苹果支付

支付完成后可以看到可以正常获取到交易的ID和交易的验证收据,为了制止被第三方恶意刷购买接口来进行非法购买,发起将该收据上传后端服务器进行验证,验证通过之后再去更新用户的购买信息。

Ok ,集成完毕,功德+1
四、 服务器校验相关流程

为什么要加后端校验?客户端支付乐成了,服务端怎么知道,万一用接口的方式通讯,假如被抓包岂不是可以无穷加金币了。太不安全了,所以才有服务器校验这一步。
iOS的校验不用说,很简单,拿到支付完成的票据直接发起请求即可,而 Android 的服务端校验就相对麻烦,须要设置谷歌云,以及对应的通行权限。
谷歌结算文档:https://developer.android.com/google/play/billing?hl=zh-cn
谷歌支付校验AI:https://developers.google.com/android-publisher/api-ref/rest/v3/purchases.products?hl=zh-cn
假如我们直接在API中调用校验接口,那肯定是直接报错:
  1. {
  2.   "error": {
  3.     "code": 403,
  4.     "message": "The project id used to call the Google Play Developer API has not been linked in the Google Play Developer Console.",
  5.     "errors": [
  6.       {
  7.         "message": "The project id used to call the Google Play Developer API has not been linked in the Google Play Developer Console.",
  8.         "domain": "androidpublisher",
  9.         "reason": "projectNotLinked"
  10.       }
  11.     ]
  12.   }
  13. }
复制代码
没有授权,接下来开始授权
4.1、Google Cloud关联

首先须要设置 Google Cloud 而且设置相关的账号,对应指定的应用。
点击项目的 API Access 中

假如这一步你没有 Google Cloud 账号,可以创建或关联已有的 Google Cloud 账号,这里我没有就直接创建了Google Cloud 账号。关联之后我们就能看到上图所示的画面。
我们可以直接在谷歌市场控制台中的 API Access 中直接进入谷歌云后台,也能 直接输入网址 https://code.google.com/apis/console/ 是一样的效果。
我们关联 Google Cloud 账号之后,默认就已经开通 Google Play Developer API 权限。

所以我们不须要再次去授权了。

假如觉得不保险,也能在内里搜刮 Billing ,然后启动相关的支付服务权限
4.2 、创建 web-OAuth 授权

当我们在谷歌市场的后台关联谷歌云的时候,就已经帮我们初始化了许多设置,已经都有了
我们再谷歌云后台,在APIs & auth 项中找到 Credentials,直接查看即可:

我们点击 Web 授权进去设置相关设置。
主要是设置左侧的上下两个 URI 地址,上面的设置后台域名:

下面的是固定写法,callback的地址一定是可用域名 + /oauth2callback。

创建完成之后,记得记录你的三个重要字段,client_id 和 client_secret 以及 redirect_uri ,后面会用到。
通过访问一下的网页获取到一个oauth2callback:
  1. https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/androidpublisher&response_type=code&access_type=offline&
  2. redirect_uri=https://api.whatsapp.sg/oauth2callback&client_id=816630003638-5p27m684jfpfa6sh6l9chbpreq2hg9ov.apps.googleusercontent.com
复制代码
返回一个code:
  1. https://api.whatsapp.sg/oauth2callback?code=4/CpVOd8CljO_gxTRE1M5jtwEFwf8gRD44vrmKNDi4GSS.kr-GHuseD-oZEnp6UADFXm0E0MD3FlAI
复制代码
拿到后面的 code 字段。
   code=4/CpVOd8CljO_gxTRE1M5jtwEFwf8gRD44vrmKNDi4GSS.kr-GHuseD-oZEnp6UADFXm0E0MD3FlAI
  我们手动的在 postman 之类的工具上,通过固定的参数,拿到 refresh_token(重点,后期全靠它)
  1. {
  2.         'grant_type':'authorization_code',
  3.         'code':'4/CpVOd8CljO_gxTRE1M5jtwEFwf8gRD44vrmKNDi4GSS.kr-GHuseD-oZEnp6UADFXm0E0MD3FlAI',//上一步获取的,
  4.         'client_id':'816630003638-5p27m684jfpfa6sh6l9chbpreq2hg9ov.apps.googleusercontent.com',
  5.         'client_secret':'36WnPnojshgj56uhghj-xCo',
  6.         'redirect_uri':'https://api.whatsapp.sg/oauth2callback',
  7. }
  8.    
复制代码
向以下的网址发起 Post 请求。
  1. https://accounts.google.com/o/oauth2/token
复制代码
一定要包管网络流通,只有一次机会,返回的json对象如下
  1. {
  2. "access_token" : "",
  3. "token_type" : "Bearer",
  4. "expires_in" : 3600,
  5. "refresh_token" : "1/zaaHNytlC3SEBX7F2cfrHcqJEa3KoAHYeXES6nmho"
  6. }
复制代码
refresh_token 就拿到了,注意一定要保存好,只有这一次机会,假如再次调用此接口 refresh_token 就是空了,不会返回了。
4.3、web-OAuth校验支付是否乐成

拿到这个refresh_token就可以调用真正的校验接口了,比方我们后端调用的是否支付乐成:
  1. https://androidpublisher.googleapis.com/androidpublisher/v3/applications/{packageName}/purchases/products/{productId}/tokens/{purchaseToken}?access_token={$access_token}"
复制代码
这里的packageName,productId,purchaseToken 各人都很熟悉了,就是Android 支付乐成之后返回给我们的,直接通报给后端即可,而access_token其实就是我们上面拿到的 refresh_token。
我们须要拿到第一次返回的 refresh_token 保存起来,后续以革新的方式来获取新的 refresh_token ,用于访问真正的API。
后台调用验证接口完成之后得到的对象如下:
  1. {
  2.   "kind": string,
  3.   "purchaseTimeMillis": string,
  4.   "purchaseState": integer,
  5.   "consumptionState": integer,
  6.   "developerPayload": string,
  7.   "orderId": string,
  8.   "purchaseType": integer,
  9.   "acknowledgementState": integer,
  10.   "purchaseToken": string,
  11.   "productId": string,
  12.   "quantity": integer,
  13.   "obfuscatedExternalAccountId": string,
  14.   "obfuscatedExternalProfileId": string,
  15.   "regionCode": string
  16. }
复制代码
只须要验证状态即可:
   consumptionState == 0 purchaseState == 0
  分析这个商品已经购买了,而且也没有被消耗,那么此时就可以给移动端返回true,让移动端执行消耗操作。
后端PHP的校验谷歌内购是否乐成示例代码:
  1.    public function checkGooglePay(){           $google_public_key    = "你的公钥(google后台在你的应用下获取)";           $inapp_purchase_data  = $_REQUEST['signtureTemp'];            $inapp_data_signature = $_REQUEST['signtureDataTemp'];             $key        = "-----BEGIN PUBLIC KEY-----\n".chunk_split($google_public_key, 64,"\n").'-----END PUBLIC KEY-----';            $key        = openssl_pkey_get_public($key);             $signature  = base64_decode($inapp_data_signature);            $ok         = openssl_verify($inapp_purchase_data,$signature,$key,OPENSSL_ALGO_SHA1);                if (1 == $ok) {               // 支付验证乐成!                //进行二次验证,订单查询                             // 1.获取access_token(3600秒有效期)             $access_token_url = "https://accounts.google.com/o/oauth2/token
  2. ";            $data_tmp2 = array(                 'grant_type'=>'refresh_token',                 'refresh_token'=>'',//长效token                 'client_id'=>'',    //客户端id                    'client_secret'=>'',//客户端密钥                 );             $http = new http($access_token_url,'POST',5);             $http->setContent($data_tmp2);             $result = $http->exec();            $result = json_decode($contents,true);             $access_token = $result['access_token'];             //2.通过得到access_token 就可以请求谷歌的API接口,得到订单状态             $packageName=""//包名            $productId="" //产物Id             $purchaseToken=""                      $url = "https://androidpublisher.googleapis.com/androidpublisher/v3/applications/{packageName}/purchases/products/{productId}/tokens/{purchaseToken}?access_token={$access_token}"
  3. ;               $http = new http($url,'GET',5);             $http->setContent($data);             $contents = $http->exec();             $contents = json_decode($contents,true);                          if($contents['consumptionState'] == 0 && $contents['purchaseState'] == 0){                //验证乐成  购买乐成而且没有消耗  google支付中客户端假如没有进行消耗是不能再次购买该商品                 //处理游戏逻辑 发钻石,通知客户端进行消耗             }else{                 //订单验证失败             }                      }else{               //署名验证失败          }                }
复制代码


  • 第一步是可选的,校验APK的署名,当前应用是不是谷歌市场下载的,假如不是从谷歌市场下载的那么支付不见效。假如你想要的校验APK泉源就加上,不想校验也可以。
  • 第二步就是开始校验谷歌内购支付订单的状态,拿到本地长期保存的refresh_token 以及之前获取到的client_id 和 client_secret 就可以到哪授权的 access_token 。
  • 第三部就是拿到 access_token 以及 客户端通报的包名,产物id,支付凭据,调用校验接口,拿到订单的当前状态。
    然后就是根据订单的状态判断返回给客户端是否有效,让客户端执行消耗操作。
    假如您觉得有须要,也可以消耗之后再次调用接口校验,是否已购买,是否已消耗。
4.4、 创建Service Account的授权

其实之前的之前的 Web-OAuth 的方式来进行验证不是不行,但是步骤相对比力复杂,而更保举的方式则是创建服务的方式来进行校验。
我们把视角拉回谷歌市场控制台,找到 Api Access 选项

其实我们在下面的访问权限就可以看到 Service Account 的选项。假如你已有 Service Account 就可以看到全部的关联的 Service Account 。假如你没有此服务,那么就可以点击创建服务去谷歌云创建。当我们到谷歌云内里点击创建 Service Account:

我们点击创建 Service Account 会走到创建服务的流程:

第一步任意写,关键是第二步:

选择角色为 Service Account Admin

第三步不填,直接提交:

你就能看到你创建的服务啦,接下来就是创建Key,Json的方式创建,然后下载到Json给到后台职员。

再下一步就回到谷歌商店控制台的 Api Access 看 Service Account 是否已经关联上了:

假如有如许的信息,分析关联上了,才是精确的流程,假如你创建了 Service Account,但是这里并没有展示,那么就肯定会错:
  1. {  
  2.   "code" : 401,  
  3.   "errors" : [ {  
  4.     "domain" : "androidpublisher",  
  5.     "message" : "The current user has insufficient permissions to perform the requested operation.",  
  6.     "reason" : "permissionDenied"  
  7.   } ],  
  8.   "message" : "The current user has insufficient permissions to perform the requested operation."  
  9. }
复制代码
之后正常显示了服务,分析你的服务才能访问到谷歌市场这一边,接下来就是点击授予访问权限。
重点是要把财务信息的两项勾选上,如许才能访问到应用内支付校验的相关权限,如图所示:

点击保存修改之后就完成了,由于我们关联账号的时候已经勾选了 Google Play Android Developer API 权限,我们如今直接就能用了。
后端的用法各平台的使用方式差别,但是都是很简单的,直接集成谷歌的API,然后统共就两步,第一步设置Config属性把这个 Service Account 生成的Json文件传入,第二步直接调用 GoogleAPI 内置的校验方法即可,都是API内置了的更方便。
当我们客户端把packageName ,prodectId,purchaseToken 三个字段传给后端,他们直接调用 API 就能直接校验,相比 Web-OAuth 的方式要更简单一些。
校验结果如下:

OK,两种方法 Web-OAuth 的授权方式,以及 Service Account 的授权方式,两种都可以到达效果,用哪种都可以。
至此谷歌内购全部流程已竣事。
丢单问题处理

使用
_inAppPurchase.purchaseStream是用来监听消息队列的回调的,也就是全部订单的状态以及信息回调,in_app_purchase这个属性的文档中这么说到:
   IMPORTANT! You must subscribe to this stream as soon as your app launches,
preferably before returning your main App Widget in main(). Otherwise you
will miss purchase updated made before this stream is subscribed to.
重要!你必须在应用程序启动后立即订阅此流,
最好在main()中返回主应用程序小部件之前。
否则你将错过订阅此流之前更新的购买。
  也就是说当我们的App在第一次启动的时候可以订阅此流来完成补单的操作,但是假如用户是之前丢单了,然后把App又卸载了,再次下载打开App后并没有进行登录操作,那用户的登录信息都拿不到怎么进行补单操作呢?
补单解决方案

让后端出一个补单的接口,在补单时只须要传一个订单号即可,那App都删除了,之前的订单号客户端怎么获取呢?使用flutter_keychain来实现,flutter_keychain就是使用的iOS的钥匙串来实现的,当用户在苹果服务器下单时,在钥匙串中保存后端生成的订单号,然后再商品乐成发货后删除钥匙串内里的订单号,完成一个完整的购买过程,再购买时任何一环出了问题钥匙串内里缓存的订单号都不会被清空,如许在App下一次启动时,在首页或者main函数中使用_inAppPurchase.purchaseStream 监听,在拿到flutter_keychain中保存的订单号完成补单过程。
注意点

1.在完成苹果服务器付款流程后通知到本身服务器接口也就是验单的接口返回的是乐成或者不乐成都要调用_inAppPurchase.completePurchase(purchaseDetails)这个方法,否则下次就掉不起苹果支付来了,当然肯定会在失败的判断内里写明白让用户本身去走苹果退款流程的文案(概率较小,但是也得考虑)
2.商品范比方果是非消耗品的话,在下单完之后一定写一个按钮供点击调用复原的方法,要是不复原的话每次下的订单,订单号都是一样的(须要注意下)。
参考:

Flutter插件:
pay 2.0.0 :https://pub.dev/packages/pay
in_app_purchase 3.2.0:https://pub.dev/packages/in_app_purchase
Google pay:
https://developers.google.com/pay/api/android/overview?hl=zh-cn
https://developer.android.com/google/play/billing
Apple pay:
https://developer.apple.com/documentation/passkit_apple_pay_and_wallet/apple_pay/setting_up_apple_pay
https://developer.apple.com/in-app-purchase/
教程:
https://juejin.cn/post/7290009513470623800
https://juejin.cn/post/7020651416276434958
https://blog.csdn.net/mumubumaopao/article/details/136112183
https://juejin.cn/post/7233310081809760317

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

徐锦洪

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表