A robust Flutter package enabling seamless integration of the PortOne V2 payment gateway into Flutter applications. Designed for simplicity and flexibility, it leverages PortOne's browser SDK, providing an intuitive payment workflow directly within your app using Flutter's InAppWebView.
Important Note: This is a community-maintained package and is not officially affiliated with PortOne.
-
Integrated Payment Flow: Simplified embedding of PortOne's payment gateway via a customizable WebView interface.
-
Flexible Payment Gateway Selection: Choose your payment gateway (
PGCompany
) when constructing thePaymentRequest
to tailor behavior and supported methods. -
PayMethod Validation: Ensures that the selected
payMethod
is supported by the chosenPGCompany
, throwing a clear error if not. -
URL Normalization & Custom Scheme Support: Normalizes redirect URLs and custom app schemes for consistent deep-link behavior in
InAppWebView
(removed automatic deep-link handling viaapp_links
dependency). -
Customizable UI: Easily configure your loading state UI and customize the payment experience.
-
Comprehensive Error Management: Provides clear error handling through callbacks for debugging and user feedback.
❗ Requirements: Flutter SDK installed on your machine.
Add the dependency to your pubspec.yaml
:
dependencies:
portone_flutter_v2: ^1.1.0
Or via command line:
flutter pub add portone_flutter_v2
From version 1.1.0 onwards, the manifest-merger plugin for the Flutter plugin is used, so you don't need to add it manually.
Android (AndroidManifest.xml
)
Ensure your app requests internet permission and declares package visibility for payment-related apps:
<uses-permission android:name="android.permission.INTERNET" />
<queries>
<!-- EASY_PAY -->
<package android:name="com.kftc.bankpay.android" /> <!-- 뱅크페이 -->
<package android:name="com.nhnent.payapp" /> <!-- 페이코 -->
<package android:name="com.lottemembers.android" /> <!-- LPAY -->
<package android:name="com.ssg.serviceapp.android.egiftcertificate" /> <!-- SSGPAY -->
<package android:name="com.inicis.kpay" /> <!-- KPAY -->
<package android:name="com.tmoney.tmpay" /> <!-- 티머니페이 -->
<package android:name="viva.republica.toss" /> <!-- 토스페이 -->
<package android:name="com.samsung.android.spay" /> <!-- 삼성페이 -->
<package android:name="com.kakao.talk" /> <!-- 카카오페이 -->
<package android:name="com.nhn.android.search" /> <!-- 네이버 -->
<package android:name="com.mysmilepay.app" /> <!-- 스마일페이 -->
<!-- CARD -->
<package android:name="kvp.jjy.MispAndroid320" /> <!-- ISP페이북 -->
<package android:name="com.kbcard.cxh.appcard" /> <!-- KBPay -->
<package android:name="com.kbstar.reboot" /> <!-- liiv -->
<package android:name="com.kbstar.kbbank" /> <!-- liiv -->
<package android:name="com.samsung.android.spaylite" /> <!-- 삼성페이니 -->
<package android:name="com.nhnent.payapp" /> <!-- 페이코 -->
<package android:name="com.lge.lgpay" /> <!-- 엘지페이 -->
<package android:name="com.hanaskcard.paycla" /> <!-- 하나 -->
<package android:name="kr.co.hanamembers.hmscustomer" /> <!-- 하나멤버스 -->
<package android:name="com.hanaskcard.rocomo.potal" /> <!-- 하나공인인증 -->
<package android:name="kr.co.citibank.citimobile" /> <!-- 씨티모바일 -->
<package android:name="com.lcacApp" /> <!-- 롯데 -->
<package android:name="kr.co.samsungcard.mpocket" /> <!-- 삼성 -->
<package android:name="com.shcard.smartpay" /> <!-- 신한 -->
<package android:name="com.shinhancard.smartshinhan" /> <!-- 신한(ARS/일반/smart) -->
<package android:name="com.shinhan.smartcaremgr" /> <!-- 신한 SOL -->
<package android:name="com.hyundaicard.appcard" /> <!-- 현대 -->
<package android:name="nh.smart.nhallonepay" /> <!-- 농협 -->
<package android:name="nh.smart.card" /> <!-- 농협 -->
<package android:name="net.ib.android.smcard" /> <!-- 삼성 모니모 -->
<package android:name="kr.co.citibank.citimobile" /> <!-- 씨티 -->
<package android:name="com.wooricard.smartapp" /> <!-- 우리WON카드 -->
<package android:name="com.wooribank.smart.npib" /> <!-- 우리WON뱅킹 -->
<!-- SECURITY -->
<package android:name="com.TouchEn.mVaccine.webs" /> <!-- TouchEn -->
<package android:name="com.ahnlab.v3mobileplus" /> <!-- V3 -->
<package android:name="kr.co.shiftworks.vguardweb" /> <!-- vguard -->
<!-- CERTIFICATE -->
<package android:name="com.hanaskcard.rocomo.potal" /> <!-- 하나 -->
<package android:name="com.lumensoft.touchenappfree" /> <!-- 현대 -->
<!-- TRANSFER -->
<package android:name="com.kftc.bankpay.android" /> <!-- 뱅크페이 -->
<package android:name="kr.co.kfcc.mobilebank" /> <!-- MG 새마을금고 -->
<package android:name="com.nh.cashcardapp" /> <!-- 뱅크페이 -->
<package android:name="com.knb.psb" /> <!-- BNK경남은행 -->
<package android:name="com.lguplus.paynow" /> <!-- 페이나우 -->
<package android:name="com.kbankwith.smartbank" /> <!-- 케이뱅크 -->
<!-- GLOBAL -->
<package android:name="com.eg.android.AlipayGphone" /> <!-- 페이나우 -->
<!-- ETC -->
<package android:name="com.sktelecom.tauth" /> <!-- SKT PASS -->
<package android:name="com.lguplus.smartotp" /> <!-- LGU PASS -->
<package android:name="com.kt.ktauth" /> <!-- KT PASS -->
<package android:name="kr.danal.app.damoum" /> <!-- 다날 다모음 -->
<package android:name="com.shinhan.sbanking" /> <!-- 신한 SOL뱅크 -->
</queries>
Declare URL schemes for your custom app scheme and payment apps:
<key>LSApplicationQueriesSchemes</key>
<array>
<!-- Include payment-related schemes you need -->
<string>alipays</string>
<string>ansimclickipcollect</string>
<string>ansimclickscard</string>
<string>chaipayment</string>
<string>citicardappkr</string>
<string>citimobileapp</string>
<string>citispay</string>
<string>cloudpay</string>
<string>com.wooricard.wcard</string>
<string>hanamopmoasign</string>
<string>hanawalletmembers</string>
<string>hdcardappcardansimclick</string>
<string>hyundaicardappcardid</string>
<string>ispmobile</string>
<string>itms-apps</string>
<string>kakaobank</string>
<string>kakaokompassauth</string>
<string>kakaolink</string>
<string>kakaoplus</string>
<string>kakaotalk</string>
<string>kb-acp</string>
<string>kb-auth</string>
<string>kb-screen</string>
<string>kbbank</string>
<string>kftc-bankpay</string>
<string>ktauthexternalcall</string>
<string>lguthepay-xpay</string>
<string>liivbank</string>
<string>line</string>
<string>lmslpay</string>
<string>lotteappcard</string>
<string>lottesmartpay</string>
<string>lpayapp</string>
<string>mailto</string>
<string>monimopay</string>
<string>monimopayauth</string>
<string>mpocket.online.ansimclick</string>
<string>naversearchthirdlogin</string>
<string>newliiv</string>
<string>newsmartpib</string>
<string>NewSmartPib</string>
<string>nhallonepayansimclick</string>
<string>nhappcardansimclick</string>
<string>nonghyupcardansimclick</string>
<string>payco</string>
<string>samsungpay</string>
<string>scardcertiapp</string>
<string>shinhan-sr-ansimclick-lpay</string>
<string>shinhan-sr-ansimclick-mola</string>
<string>shinhan-sr-ansimclick-naverpay</string>
<string>shinhan-sr-ansimclick-payco</string>
<string>shinhan-sr-ansimclick</string>
<string>shinsegaeeasypayment</string>
<string>smailapp</string>
<string>smhyundaiansimclick</string>
<string>sms</string>
<string>smshinhanansimclick</string>
<string>supertoss</string>
<string>tauthlink</string>
<string>ukbanksmartbanknonloginpay</string>
<string>upluscorporation</string>
<string>v3mobileplusweb</string>
<string>vguardstart</string>
<string>weixin</string>
<string>wooripay</string>
</array>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<!-- PaymentRequest.appScheme -->
<string>yourappscheme</string>
</array>
</dict>
</array>
Replace yourappscheme
with your application's custom scheme used for deep-linking.
Here’s how to create a PaymentRequest
with a specific PG company and pay method:
import 'package:flutter/material.dart';
import 'package:portone_flutter_v2/portone_flutter_v2.dart';
class PaymentScreen extends StatelessWidget {
const PaymentScreen({super.key, required this.paymentId});
final String paymentId;
@override
Widget build(BuildContext context) {
final paymentRequest = PaymentRequest(
storeId: 'store-00000000-0000-0000-0000-000000000000',
paymentId: paymentId,
orderName: 'Flutter Course',
totalAmount: 15000,
currency: PaymentCurrency.KRW,
channelKey: 'channel-key-00000000-0000-0000-0000-000000000000',
payMethod: PaymentPayMethod.card,
appScheme: 'yourappscheme', // Your app's URL scheme
pg: PGCompany.niceV2, // specify PG company (Optional)
);
return Scaffold(
appBar: AppBar(title: const Text('Make a Payment')),
body: PortonePayment(
data: paymentRequest,
initialChild: const Center(child: CircularProgressIndicator()),
callback: (PaymentResponse result) {
// Handle successful payment
debugPrint('Payment Success: ${result.toJson()}');
Navigator.pop(context, result);
},
onError: (Object? error) {
// Handle payment error
debugPrint('Payment Error: $error');
Navigator.pop(context, null);
},
),
);
}
}
// Navigate to payment screen and wait for result
final result = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => const PaymentScreen()),
);
if (result != null) {
// Payment succeeded
final response = result as PaymentResponse;
debugPrint('Transaction successful: ${response.paymentId}');
} else {
// Payment failed or was cancelled
debugPrint('Payment was cancelled or encountered an error.');
}
Internally, this package:
- Uses
flutter_inappwebview
to embed PortOne's browser SDK securely. - Dynamically constructs and loads the payment initiation HTML with user-provided payment details.
- Manages redirects and payment results by intercepting URL navigations and intents, offering a streamlined mobile payment experience.
If you try an unsupported combination, e.g., PGCompany.niceV2
with PaymentPayMethod.convenienceStore
, the constructor will throw an ArgumentError
:
test('throws ArgumentError when pg does not support given payMethod', () {
expect(
() => PaymentRequest(
storeId: 'store-00000000-0000-0000-0000-000000000000',
paymentId: 'payment-unsupported',
orderName: 'Unsupported Method',
totalAmount: 500,
currency: PaymentCurrency.KRW,
payMethod: PaymentPayMethod.convenienceStore, // niceV2 에는 없음
appScheme: 'portoneTest',
pg: PGCompany.niceV2, // niceV2 의 methods 에 convenienceStore 가 없음
),
throwsA(
isA<ArgumentError>()
.having(
(e) => e.name,
'error name',
'payMethod',
)
.having(
(e) => e.message,
'error message',
contains('지원되지 않는 결제수단입니다'),
),
),
);
});
See PaymentSupportedMethods
extension for details. Example for `niceV2
...
List<PaymentPayMethod> get methods {
return switch (this) {
// https://developers.portone.io/opi/ko/integration/pg/v2/nice-v2?v=v2
PGCompany.niceV2 => <PaymentPayMethod>[
PaymentPayMethod.card,
PaymentPayMethod.transfer,
PaymentPayMethod.virtualAccount,
PaymentPayMethod.mobile,
PaymentPayMethod.easyPay,
PaymentPayMethod.giftCertificate,
],
...
Use this list to verify supported methods before creating a request, or rely on built-in validation.
Your contributions can enhance this open-source project. You can help by:
- Reporting issues or suggesting enhancements.
- Improving documentation and clarity.
- Submitting pull requests for bug fixes or additional features.
See the contributing guidelines before opening pull requests.
Execute unit tests:
flutter test --test-randomize-ordering-seed random
For detailed coverage reports:
flutter test --coverage --test-randomize-ordering-seed random
genhtml coverage/lcov.info -o coverage/
open coverage/index.html
MIT License - see LICENSE.