portone_flutter_v2 1.1.0
portone_flutter_v2: ^1.1.0 copied to clipboard
A module for integrating PortOne V2 payment service in Flutter App.
PortOne Flutter V2 #
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.
π¦ Key Features #
-
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.
π Getting Started #
Installation π» #
β 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
Platform Configuration #
Android (AndroidManifest.xml
)
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>
iOS (Info.plist
)
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.
π§ Usage Example #
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);
},
),
);
}
}
Handling the Payment Result #
// 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.');
}
π Package Internal Logic #
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.
π§© Supported Payment Methods per PG Company #
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.
π€ Contributing #
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.
π§ͺ Testing & Code Coverage #
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
π License #
MIT License - see LICENSE.