Flutter支付集成插件toss_payment的使用

发布于 1周前 作者 htzhanglong 来自 Flutter

Flutter支付集成插件toss_payment的使用

本README描述了如何在Flutter应用中使用toss_payment插件来实现支付功能。有关如何编写好的包README,请参阅Dart指南,以及有关开发包的一般信息,请参阅Dart指南Flutter指南

Toss Payments Flutter插件

该插件旨在帮助希望在Flutter应用中使用Toss Payments进行应用内支付的开发者。有关Toss Payments的详细信息,请访问官方文档

功能

  1. 使用支付窗口和webview连接外部应用。

开始使用

为了实现外部应用的连接,需要根据不同的操作系统进行环境设置。

1. iOS

Info.plist文件中添加用于启动外部应用的Scheme。

<key>LSApplicationQueriesSchemes</key>
<array>
    <string>supertoss</string> <!-- Toss Pay -->
    <string>mpocket.online.ansimclick</string> <!-- Samsung Card App -->
    <string>hdcardappcardansimclick</string> <!-- Hyundai Card App -->
    <string>smhyundaiansimclick</string> <!-- Hyundai Card Certificate -->
    <string>wooripay</string> <!-- Woori Card App -->
    <string>shinhan-sr-ansimclick</string> <!-- Shinhan Card App -->
    <string>smshinhanansimclick</string> <!-- Shinhan Card Certificate -->
    <string>kb-acp</string> <!-- Kookmin Card App -->
    <string>lottesmartpay</string> <!-- Lotte Card Mobile Payment -->
    <string>lotteappcard</string> <!-- Lotte Card App -->
    <string>cloudpay</string> <!-- Hana Card App -->
    <string>nhappvardansimclick</string> <!-- NongHyup Card App -->
    <string>nonghyupcardansimclick</string> <!-- NongHyup Card Certificate -->
    <string>citispay</string> <!-- Citibank Card App -->
    <string>citicardappkr</string> <!-- Citibank Card Certificate -->
    <string>ispmobile</string> <!-- ISP Mobile -->
</array>

3. Android

a. 设置最小SDK版本为20及以上

android/app/build.gradle文件中设置defaultConfig

defaultConfig {
    ...
    minSdkVersion 20
    ...
}
b. 在manifest文件中添加Scheme和权限

android/app/main/AndroidManifest.xml文件中添加Scheme和权限。

<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />

<queries>
    <!-- Toss -->
    <package android:name="viva.republica.toss" />
    <intent>
        <action android:name="android.intent.action.VIEW" />
        <data android:scheme="supertoss" />
    </intent>
    <!-- Samsung Card -->
    <package android:name="kr.co.samsungcard.mpocket" />
    <intent>
        <action android:name="android.intent.action.VIEW" />
        <data android:scheme="mpocket.online.ansimclick" />
    </intent>
    <!-- Hyundai Card -->
    <package android:name="com.hyundaicard.appcard" />
    <intent>
        <action android:name="android.intent.action.VIEW" />
        <data android:scheme="hdcardappcardansimclick" />
    </intent>
    <!-- Hyundai Card Certificate -->
    <intent>
        <action android:name="android.intent.action.VIEW" />
        <data android:scheme="smhyundaiansimclick" />
    </intent>
    <!-- Woori Card App -->
    <intent>
        <action android:name="android.intent.action.VIEW" />
        <data android:scheme="wooripay" />
    </intent>
    <!-- Shinhan Card App -->
    <intent>
        <action android:name="android.intent.action.VIEW" />
        <data android:scheme="shinhan-sr-ansimclick" />
    </intent>
    <!-- Shinhan Card Certificate -->
    <intent>
        <action android:name="android.intent.action.VIEW" />
        <data android:scheme="smshinhanansimclick" />
    </intent>
    <!-- Kookmin Card App -->
    <intent>
        <action android:name="android.intent.action.VIEW" />
        <data android:scheme="kb-acp" />
    </intent>
    <!-- Lotte Card Mobile Payment -->
    <intent>
        <action android:name="android.intent.action.VIEW" />
        <data android:scheme="lottesmartpay" />
    </intent>
    <!-- Lotte Card App -->
    <intent>
        <action android:name="android.intent.action.VIEW" />
        <data android:scheme="lotteappcard" />
    </intent>
    <!-- Hana Card App -->
    <intent>
        <action android:name="android.intent.action.VIEW" />
        <data android:scheme="cloudpay" />
    </intent>
    <!-- NongHyup Card App -->
    <intent>
        <action android:name="android.intent.action.VIEW" />
        <data android:scheme="nhappvardansimclick" />
    </intent>
    <!-- NongHyup Card Certificate -->
    <intent>
        <action android:name="android.intent.action.VIEW" />
        <data android:scheme="nonghyupcardansimclick" />
    </intent>
    <!-- Citibank Card Certificate -->
    <intent>
        <action android:name="android.intent.action.VIEW" />
        <data android:scheme="citicardappkr" />
    </intent>
    <!-- ISP Mobile -->
    <intent>
        <action android:name="android.intent.action.VIEW" />
        <data android:scheme="ispmobile" />
    </intent>
</queries>

额外提示

a. 如果无法访问Android Web

android:usesCleartextTraffic设置为true

<application
  ...
  android:usesCleartextTraffic="true">
...
</application>
b. 如果在Android 12及以上系统中应用无法安装或运行

android:exported设置为true

<activity
  android:name=".MainActivity"
  ...
  android:exported="true">
...
</activity>

使用方法

通过PaymentWebView可以连接支付窗口。

PaymentWebView(title: "支付页面标题", paymentRequestUrl: Uri.parse("支付网页地址"))

其他信息

注意事项

  • 示例应用中使用了Mock Server来模拟Web页面。实际使用时,请创建自己的Web页面并使用。

路线图

  • 测试代码。
  • 其他。

完整示例代码

import 'dart:developer' as dev;

import 'package:flutter/material.dart';
import 'package:toss_payment/toss_payment.dart';

import 'models/payment_request.dart';
import 'models/product.dart';
import 'services/mock_server.dart';
import 'widgets/order_widget.dart';
import 'widgets/product_widget.dart';

void main() async {
  /// 示例中使用的mock server
  await MockServer.startServer();

  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // 应用的根组件
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Toss Payment Demo',
      theme: ThemeData(
        primarySwatch: Colors.indigo,
      ),
      home: const MyHomePage(title: 'Flutter Toss Payment Demo Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  [@override](/user/override)
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final Product _product = Product(price: 15000, name: 'Toss T-shirt');

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: SafeArea(
        child: Column(children: [
          Expanded(
            child: Center(
              child: ProductWidget(
                product: _product,
              ),
            ),
          ),
          GridView.count(
            shrinkWrap: true,
            padding: const EdgeInsets.all(4),
            crossAxisCount: 3,
            children: List.generate(9, (index) {
              Widget ret = Container(
                margin: const EdgeInsets.all(4),
                decoration: BoxDecoration(
                  border: Border.all(color: Colors.grey.shade300),
                  borderRadius: const BorderRadius.all(
                    Radius.circular(8),
                  ),
                ),
              );

              switch (index) {
                case 0:
                  ret = OrderWidget(
                    title: '信用卡',
                    product: _product,
                    onTap: (request) {
                      _showPayment(context, request);
                    },
                    payBy: '信用卡',
                  );
                  break;
                case 1:
                  ret = OrderWidget(
                    title: '自动扣款',
                    product: _product,
                    onTap: (request) {
                      _showPayment(context, request);
                    },
                    payBy: '自动扣款',
                  );
                  break;
                case 2:
                  ret = OrderWidget(
                    title: '直接打开卡应用',
                    product: _product,
                    onTap: (request) {
                      _showPayment(context, request);
                    },
                    payBy: '直接打开卡应用',
                  );
                  break;
                case 3:
                  ret = OrderWidget(
                    title: '虚拟账户',
                    product: _product,
                    onTap: (request) {
                      _showPayment(context, request);
                    },
                    payBy: '虚拟账户',
                  );
                  break;
                case 4:
                  ret = OrderWidget(
                    title: '银行转账',
                    product: _product,
                    onTap: (request) {
                      _showPayment(context, request);
                    },
                    payBy: '银行转账',
                  );
                  break;
                case 5:
                  ret = OrderWidget(
                    title: '手机支付',
                    product: _product,
                    onTap: (request) {
                      _showPayment(context, request);
                    },
                    payBy: '手机支付',
                  );
                  break;
                case 6:
                  ret = OrderWidget(
                    title: '图书文化券',
                    product: _product,
                    onTap: (request) {
                      _showPayment(context, request);
                    },
                    payBy: '图书文化券',
                  );
                  break;
                case 7:
                  ret = OrderWidget(
                    title: 'Toss支付',
                    product: _product,
                    onTap: (request) {
                      _showPayment(context, request);
                    },
                    payBy: 'Toss支付',
                  );
                  break;
              }
              return ret;
            }),
          ),
        ]),
      ),
    );
  }

  _showPayment(BuildContext context, PaymentRequest request) async {
    var ret = await showModalBottomSheet(
        context: context,
        isScrollControlled: true,
        backgroundColor: Colors.transparent,
        enableDrag: false,
        isDismissible: false,
        builder: (context) {
          bool success = false;
          return Container(
            margin: const EdgeInsets.only(top: 110),
            child: PaymentWebView(
              title: _product.name,
              paymentRequestUrl: request.url,
              onPageStarted: (url) {
                dev.log('onPageStarted.url = $url', name: "PaymentWebView");
              },
              onPageFinished: (url) {
                dev.log('onPageFinished.url = $url', name: "PaymentWebView");
                // TODO 判断支付是否成功
                success = url.contains('success');
              },
              onDisposed: () {},
              onTapCloseButton: () {
                Navigator.of(context).pop(success);
              },
            ),
          );
        });
    dev.log('ret = $ret', name: '_showPayment');
  }
}

extension PaymentRequestExtension on PaymentRequest {
  Uri get url {
    // TODO 请替换为您的Toss Pay Web地址。以下仅为示例。
    return Uri.http("localhost:8080", "payment", json);
  }
}

更多关于Flutter支付集成插件toss_payment的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter支付集成插件toss_payment的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter项目中集成toss_payment插件以实现支付功能,可以遵循以下步骤和代码示例。toss_payment插件通常用于集成Toss支付服务,但请注意,实际使用时需要确保你已经获得了Toss支付的相关API密钥和配置。

以下是一个基本的代码示例,展示如何在Flutter应用中集成toss_payment插件:

1. 添加依赖

首先,在你的pubspec.yaml文件中添加toss_payment依赖:

dependencies:
  flutter:
    sdk: flutter
  toss_payment: ^最新版本号  # 替换为实际的最新版本号

然后运行flutter pub get来安装依赖。

2. 配置Android和iOS

根据你使用的平台,你可能需要在AndroidManifest.xmlInfo.plist文件中添加一些配置,以便正确初始化支付插件。这通常包括设置支付相关的URL Scheme等。具体配置请参考toss_payment插件的官方文档。

3. 初始化插件和发起支付

在你的Flutter应用中,你可以通过以下代码初始化toss_payment插件并发起支付请求:

import 'package:flutter/material.dart';
import 'package:toss_payment/toss_payment.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Toss Payment Integration'),
        ),
        body: Center(
          child: ElevatedButton(
            onPressed: _initiatePayment,
            child: Text('Pay with Toss'),
          ),
        ),
      ),
    );
  }

  Future<void> _initiatePayment() async {
    // 配置支付参数
    final TossPaymentConfig config = TossPaymentConfig(
      merchantId: '你的商户ID', // 替换为你的实际商户ID
      mid: '你的MID', // 替换为你的实际MID
      tid: '你的TID', // 替换为你的实际TID
      key: '你的API密钥', // 替换为你的实际API密钥
      amount: 1000, // 支付金额,单位通常是韩元(根据Toss的具体要求调整)
      currency: 'KRW', // 货币代码
      name: '商品名称', // 商品名称
      buyerEmail: '买家邮箱', // 买家邮箱
      buyerName: '买家姓名', // 买家姓名
      buyerPhone: '买家电话', // 买家电话
      // 根据需要添加更多配置参数
    );

    // 初始化Toss支付插件
    final TossPayment tossPayment = TossPayment();

    try {
      // 发起支付请求
      final result = await tossPayment.startPayment(config: config);
      if (result != null && result.isSuccess) {
        // 支付成功处理
        print('支付成功: ${result.data}');
      } else {
        // 支付失败或取消处理
        print('支付失败或取消: ${result?.error?.message}');
      }
    } catch (e) {
      // 处理异常
      print('支付过程中发生错误: $e');
    }
  }
}

注意事项

  1. API密钥和敏感信息:确保不要在代码中硬编码API密钥或其他敏感信息。考虑使用环境变量或安全的密钥管理服务。
  2. 错误处理:在实际应用中,添加更详细的错误处理逻辑,以提供更好的用户体验。
  3. 支付回调:根据你的业务需求,处理支付成功或失败后的回调逻辑,比如更新订单状态、发送通知等。
  4. UI/UX:调整UI/UX以适应你的应用风格和用户体验需求。

请确保参考toss_payment插件的官方文档和Toss支付的API文档,以获取最新的配置要求和API变化。

回到顶部