RxReader / alipay_kit

Flutter版支付宝登录/支付
MIT License
313 stars 56 forks source link

周期扣款 #55

Closed droplet-js closed 1 year ago

droplet-js commented 1 year ago

周期扣款

支付后签约

和普通支付一个流程

先签约后代扣

后端设置 return_url: ${your app scheme}://${custom host}/alipay/page_sign/return_url

Future<bool> _showAlipayPageSignAlert(BuildContext context, {required String url}) async {
  final dynamic result = await showCupertinoDialog(
    context: context,
    builder: (BuildContext context) {
      return WillPopScope(
        child: CupertinoAlertDialog(
          content: SizedBox(
            height: 40,
            child: Stack(
              children: <Widget>[
                Visibility(
                  visible: false,
                  maintainState: true,
                  maintainAnimation: true,
                  maintainSize: true,
                  maintainInteractivity: false,
                  child: AlipayPageSignSlientExec(url: url),
                ),
                Center(
                  child: CupertinoActivityIndicator(radius: 12),
                ),
              ],
            ),
          ),
        ),
        onWillPop: () => Future<bool>.value(false),
      );
    },
    barrierDismissible: false,
  );
  return result is bool && result;
}

// 先签约后扣费
// https://opendocs.alipay.com/open/00a05b?pathHash=4f048b01#%E6%97%A0%E7%BA%BF%E7%AB%AF%E5%94%A4%E8%B5%B7%E7%AD%BE%E7%BA%A6%E8%AF%B4%E6%98%8E
// url 后端处理
// [encodeURIComponent](https://blog.csdn.net/KokJuis/article/details/84140514)
// final String signParams = url.replace("https://openapi.alipay.com/gateway.do?","");
// url = "alipays://platformapi/startapp?appId=60000157&appClearTop=false&startMultApp=YES&sign_params="+ encodeURIComponent(signParams)
// url 客户端处理
// final String signParams = url.replaceAll('https://openapi.alipay.com/gateway.do?', '');
// url = 'alipays://platformapi/startapp?appId=60000157&appClearTop=false&startMultApp=YES&sign_params=${Uri.encodeQueryComponent(signParams)}';
class AlipayPageSignSlientExec extends StatefulWidget {
  const AlipayPageSignSlientExec({
    super.key,
    required this.url,
  });

  final String url;

  @override
  State<StatefulWidget> createState() {
    return _AlipayPageSignSlientExecState();
  }
}

class _AlipayPageSignSlientExecState extends State<AlipayPageSignSlientExec> with WidgetsBindingObserver {
  Route<dynamic>? _route;
  late final StreamSubscription<String> _linkClickSubs;

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
    _linkClickSubs = LinkKitPlatform.instance.linkClickStream().listen((String event) {
      _evalDeepLink(context, event);
    });
    launchUrlString(widget.url, mode: LaunchMode.externalApplication);
  }

  // ${your app scheme}://${custom host}/alipay/page_sign/return_url?msg=Success&charset=utf-8&code=10000&method=alipay.user.agreement.page.sign.return
  Future<void> _evalDeepLink(BuildContext context, String url) async {
    final Uri uri = Uri.parse(url);
    if (uri.path == '/alipay/page_sign/return_url') {
      if (kDebugMode) {
        print('Alipay Page Sign: $url');
      }
      if (uri.queryParameters['code'] == '10000') {
        if (_route?.isActive ?? false) {
          Navigator.of(context).pop(true);
        }
      } else {
        if (_route?.isActive ?? false) {
          Navigator.of(context).pop(false);
        }
      }
    }
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    if (state == AppLifecycleState.resumed) {
      _clearSubscription();
    }
  }

  Future<void> _clearSubscription() async {
    await Future<void>.delayed(const Duration(seconds: 1));// 取消事件不走 deep link 回调,只能在 resumed 里取消(这会与 deep link 回调冲突,故作延迟)
    if (_route?.isActive ?? false) {
      // ignore: use_build_context_synchronously
      Navigator.of(context).pop(false);
    }
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    _route ??= ModalRoute.of(context);
  }

  @override
  void dispose() {
    _linkClickSubs.cancel();
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return SizedBox();
  }
}