├── .gitignore
├── .metadata
├── CHANGELOG.md
├── LICENSE
├── README.md
├── SUPPORT.md
├── android
├── .gitignore
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
├── settings.gradle
└── src
│ └── main
│ └── kotlin
│ └── io
│ └── portone
│ └── portone_flutter
│ └── IamportFlutterPlugin.kt
├── assets
└── images
│ ├── allow-arbitrary.gif
│ ├── app-scheme-registry.gif
│ └── iamport-logo.png
├── build.yaml
├── example
├── .gitignore
├── .metadata
├── README.md
├── android
│ ├── .gitignore
│ ├── app
│ │ ├── build.gradle
│ │ └── src
│ │ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ │ ├── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── kotlin
│ │ │ │ └── io
│ │ │ │ │ └── portone
│ │ │ │ │ ├── example
│ │ │ │ │ └── MainActivity.kt
│ │ │ │ │ └── portone_flutter_example
│ │ │ │ │ └── MainActivity.kt
│ │ │ └── res
│ │ │ │ ├── drawable-v21
│ │ │ │ └── launch_background.xml
│ │ │ │ ├── drawable
│ │ │ │ └── launch_background.xml
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── values-night
│ │ │ │ └── styles.xml
│ │ │ │ └── values
│ │ │ │ └── styles.xml
│ │ │ └── profile
│ │ │ └── AndroidManifest.xml
│ ├── build.gradle.kts
│ ├── gradle.properties
│ ├── gradle
│ │ └── wrapper
│ │ │ └── gradle-wrapper.properties
│ └── settings.gradle.kts
├── assets
│ └── images
│ │ └── iamport-logo.png
├── ios
│ ├── .gitignore
│ ├── Flutter
│ │ ├── AppFrameworkInfo.plist
│ │ ├── Debug.xcconfig
│ │ └── Release.xcconfig
│ ├── Podfile
│ ├── Runner.xcodeproj
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace
│ │ │ ├── contents.xcworkspacedata
│ │ │ └── xcshareddata
│ │ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ │ └── WorkspaceSettings.xcsettings
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ └── Runner.xcscheme
│ ├── Runner.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── WorkspaceSettings.xcsettings
│ └── Runner
│ │ ├── AppDelegate.h
│ │ ├── AppDelegate.m
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ ├── Contents.json
│ │ │ ├── Icon-App-1024x1024@1x.png
│ │ │ ├── Icon-App-20x20@1x.png
│ │ │ ├── Icon-App-20x20@2x.png
│ │ │ ├── Icon-App-20x20@3x.png
│ │ │ ├── Icon-App-29x29@1x.png
│ │ │ ├── Icon-App-29x29@2x.png
│ │ │ ├── Icon-App-29x29@3x.png
│ │ │ ├── Icon-App-40x40@1x.png
│ │ │ ├── Icon-App-40x40@2x.png
│ │ │ ├── Icon-App-40x40@3x.png
│ │ │ ├── Icon-App-60x60@2x.png
│ │ │ ├── Icon-App-60x60@3x.png
│ │ │ ├── Icon-App-76x76@1x.png
│ │ │ ├── Icon-App-76x76@2x.png
│ │ │ └── Icon-App-83.5x83.5@2x.png
│ │ └── LaunchImage.imageset
│ │ │ ├── Contents.json
│ │ │ ├── LaunchImage.png
│ │ │ ├── LaunchImage@2x.png
│ │ │ ├── LaunchImage@3x.png
│ │ │ └── README.md
│ │ ├── Base.lproj
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ │ ├── Info.plist
│ │ ├── Runner-Bridging-Header.h
│ │ └── main.m
├── lib
│ ├── main.dart
│ ├── model
│ │ ├── carrier.dart
│ │ ├── method.dart
│ │ ├── pg.dart
│ │ └── quota.dart
│ └── screens
│ │ ├── certification.dart
│ │ ├── certification_result.dart
│ │ ├── certification_test.dart
│ │ ├── home.dart
│ │ ├── payment.dart
│ │ ├── payment_result.dart
│ │ └── payment_test.dart
├── manuals
│ ├── CALLBACK.md
│ └── TRANS.md
├── pubspec.yaml
└── test
│ └── widget_test.dart
├── ios
├── .gitignore
├── Assets
│ └── .gitkeep
├── Classes
│ ├── IamportFlutterPlugin.h
│ ├── IamportFlutterPlugin.m
│ └── SwiftIamportFlutterPlugin.swift
└── portone_flutter.podspec
├── lib
├── Iamport_certification.dart
├── iamport_payment.dart
├── model
│ ├── certification_data.dart
│ ├── certification_data.g.dart
│ ├── iamport_url.dart
│ ├── iamport_validation.dart
│ ├── payment_data.dart
│ ├── payment_data.g.dart
│ ├── pg
│ │ ├── bypass.dart
│ │ ├── bypass.g.dart
│ │ ├── danal
│ │ │ ├── danal.dart
│ │ │ └── danal.g.dart
│ │ ├── daou
│ │ │ ├── daou.dart
│ │ │ └── daou.g.dart
│ │ ├── kcp
│ │ │ ├── kcp_products.dart
│ │ │ └── kcp_products.g.dart
│ │ ├── naver
│ │ │ ├── naver_co_products.dart
│ │ │ ├── naver_co_products.g.dart
│ │ │ ├── naver_interface.dart
│ │ │ ├── naver_interface.g.dart
│ │ │ ├── naver_pay_products.dart
│ │ │ ├── naver_pay_products.g.dart
│ │ │ ├── naver_products.dart
│ │ │ └── naver_products.g.dart
│ │ ├── settle
│ │ │ ├── settle.dart
│ │ │ └── settle.g.dart
│ │ └── tosspayments
│ │ │ ├── tosspayments.dart
│ │ │ └── tosspayments.g.dart
│ └── url_data.dart
└── widget
│ ├── iamport_error.dart
│ └── iamport_webview.dart
├── portone_flutter.iml
└── pubspec.yaml
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .dart_tool/
3 | .atom/
4 | .idea
5 | .vscode
6 | .packages
7 | .pub/
8 | build/
9 | ios/.generated/
10 | packages
11 | pubspec.lock
12 | doc/
13 |
14 | example/ios/Podfile.lock
15 | **/Flutter/App.framework/
16 | **/Flutter/Flutter.framework/
17 | **/Flutter/Generated.xcconfig/
18 | **/Flutter/flutter_assets/
19 | example/ios/Podfile.lock
20 | example/.flutter-plugins-dependencies
21 | example/ios/Flutter/flutter_export_environment.sh
22 |
--------------------------------------------------------------------------------
/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: 7a4c33425ddd78c54aba07d86f3f9a4a0051769b
8 | channel: stable
9 |
10 | project_type: plugin
11 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [0.12.1](https://github.com/portone-io/portone_flutter/tree/main)
2 | - tierCode 결제창 호출 기능을 추가했습니다.
3 |
4 | ## [0.12.0](https://github.com/portone-io/portone_flutter/tree/v0.12.0)
5 | - Flutter 3.28 컴파일 오류의 원인인 iamport_webview_flutter 에서 v1 android embedding 제거
6 |
7 | ## [0.11.0](https://github.com/portone-io/portone_flutter/tree/v0.11.0)
8 | - portone_flutter 패키지로 이전
9 |
10 | ## [0.10.21](https://github.com/portone/portone_flutter/tree/v0.10.21)
11 | - iamport_flutter discontinued 안내 추가
12 |
13 | ## [0.10.20](https://github.com/portone/portone_flutter/tree/v0.10.20)
14 | - Flutter 3.27.0 부터 발생하는 오류를 해결했습니다.
15 |
16 | ## [0.10.19](https://github.com/portone-io/portone_flutter/tree/v0.10.19)
17 | - 안드로이드 gradle plugin 버전을 8.4.2로 업데이트했습니다.
18 | - uni_links를 app_links로 변경했습니다.
19 |
20 | ## [0.10.18](https://github.com/portone-io/portone_flutter/tree/v0.10.18)
21 | - customer_id 파라미터를 추가했습니다.
22 |
23 | ## [0.10.17](https://github.com/portone-io/portone_flutter/tree/v0.10.17)
24 | - 누락된 네이버페이 전용 파라미터들을 추가했습니다.
25 |
26 | ## [0.10.16](https://github.com/portone-io/portone_flutter/tree/v0.10.16)
27 | - 현금영수증 노출 관련 파라미터들을 추가했습니다.
28 |
29 | ## [0.10.15](https://github.com/portone-io/portone_flutter/tree/v0.10.15)
30 | - 앱 실행 방식 개선 후 오류를 해결하였습니다.
31 |
32 | ## [0.10.14](https://github.com/portone-io/portone_flutter/tree/v0.10.14)
33 | - 앱 실행 방식을 개선했습니다.
34 |
35 | ## [0.10.13](https://github.com/portone-io/portone_flutter/tree/v0.10.13)
36 | - 헥토파이낸셜 전용 파라미터를 추가했습니다.
37 |
38 | ## [0.10.12](https://github.com/portone-io/portone_flutter/tree/v0.10.12)
39 | - 본인인증시 임의의 user agent를 넣을 수 있는 기능을 추가했습니다.
40 |
41 | ## [0.10.11](https://github.com/portone-io/portone_flutter/tree/v0.10.11)
42 | - 안드로이드에서 이니시스 간편결제 아이콘이 깨지는 문제를 해결했습니다.
43 | - flutter sdk 버전 조건을 수정했습니다.
44 |
45 | ## [0.10.10](https://github.com/portone-io/portone_flutter/tree/v0.10.10)
46 | - 본인인증 pg 파라미터를 추가했습니다.
47 |
48 | ## [0.10.9](https://github.com/portone-io/portone_flutter/tree/v0.10.9)
49 | - 토스페이먼츠 전용 파라미터를 추가했습니다.
50 | - v3, KB 앱스킴들을 추가했습니다.
51 |
52 | ## [0.10.8](https://github.com/portone-io/portone_flutter/tree/v0.10.8)
53 | - 키움페이(다우, 페이조아) 전용 파라미터를 추가했습니다.
54 |
55 | ## [0.10.7](https://github.com/portone-io/portone_flutter/tree/v0.10.7)
56 | - 네이버페이 비밀번호 패드에서 클릭이 잘 되지 않는 문제를 해결했습니다.
57 |
58 | ## [0.10.6](https://github.com/portone-io/portone_flutter/tree/v0.10.6)
59 | - Javascript SDK 버전을 변경했습니다.
60 | - 토스페이먼츠를 추가했습니다.
61 |
62 | ## [0.10.5](https://github.com/portone-io/portone_flutter/tree/v0.10.5)
63 | - 코틀린 버전을 1.7.20으로 업데이트했습니다.
64 |
65 | ## [0.10.4](https://github.com/portone-io/portone_flutter/tree/v0.10.4)
66 | - 할부개월수 파라미터가 제대로 들어가도록 수정했습니다.
67 |
68 | ## [0.10.3](https://github.com/portone-io/portone_flutter/tree/v0.10.3)
69 | - kcpProducts 파라미터를 추가했습니다.
70 |
71 | ## [0.10.2](https://github.com/portone-io/portone_flutter/tree/v0.10.2)
72 | - 웹뷰 컴포넌트들에 gestureRecognizers 파라미터를 추가했습니다.
73 |
74 | ## [0.10.1+4](https://github.com/portone-io/portone_flutter/tree/v0.10.1+4)
75 | - json_serializable 및 json_annotation의 버전 제약조건을 완화했습니다.
76 |
77 | ## [0.10.1+3](https://github.com/portone-io/portone_flutter/tree/v0.10.1+3)
78 | - gradle kotlin 플러그인 버전을 업데이트했습니다.
79 | - amount 값을 int 대신 num으로 받을 수 있도록 변경했습니다.
80 |
81 | ## [0.10.1+2](https://github.com/portone-io/portone_flutter/tree/v0.10.1+2)
82 | - confirm_url 지원을 추가했습니다.
83 |
84 | ## [0.10.1+1](https://github.com/portone-io/portone_flutter/tree/v0.10.1+1)
85 | - README 예제 및 링크를 수정했습니다.
86 | - 일부 기기에서 의도치 않게 확대가 되는 기능을 차단했습니다.
87 |
88 | ## [0.10.1](https://github.com/portone-io/portone_flutter/tree/v0.10.1)
89 | - 안드로이드 12에서 웹뷰에서 키보드가 나타니지 않는 문제를 해결했습니다.
90 | - 임의의 m_redirect_url을 받고 처리할 수 있도록 수정했습니다.
91 | - 본인인증시 팝업 파라미터를 추가했습니다.(명시적 false 필요할 경우 사용)
92 |
93 | ## [0.10.0](https://github.com/portone-io/portone_flutter/tree/v0.10.0)
94 | - 예제 UI를 개선했습니다.
95 | - 로딩 컴포넌트가 보이지 않는 문제를 해결했습니다.
96 | - 로딩 컴포넌트 및 AppBar 커스터마이징 기능을 개선했습니다.
97 | - json 라이브러리를 json_serializable로 교체했습니다.
98 | - 우리페이 확대에 따른 우리WON뱅킹 지원을 추가했습니다.
99 | - [iOS] 연속적인 자바스크립트 실행으로 인해 불필요한 로그가 생성되지 않도록 수정했습니다.
100 |
101 | ## [0.10.0-dev.6](https://github.com/portone-io/portone_flutter/tree/v0.10.0-dev.6)
102 | - [안드로이드] 다날 일반결제에서 페이북을 통해 결제할 때 앱을 실행시 발생하는 오류를 해결했습니다.
103 |
104 | ## [0.10.0-dev.5](https://github.com/portone-io/portone_flutter/tree/v0.10.0-dev.5)
105 | - [안드로이드] 리뉴얼된 다날 본인인증 UI에서 PASS 앱 실행을 위한 m_redirect_url을 추가했습니다.
106 |
107 | ## [0.10.0-dev.4](https://github.com/portone-io/portone_flutter/tree/v0.10.0-dev.4)
108 | - JS SDK를 v1.2.0로 업데이트했습니다.
109 | - 스마트로를 추가했습니다.
110 |
111 | ## [0.10.0-dev.3](https://github.com/portone-io/portone_flutter/tree/v0.10.0-dev.3)
112 | - 토스 지원을 추가했습니다.
113 | - 네이버페이 결제형에서 발생하는 json에 불필요한 값이 들어가는 오류를 수정했습니다.
114 |
115 | ## [0.10.0-dev.2](https://github.com/portone-io/portone_flutter/tree/v0.10.0-dev.2)
116 | - [안드로이드] NH카드 V3 url 파싱 에러를 해결했습니다.
117 | - [ios] 리브 앱 지원을 추가했습니다.
118 | - 우리페이 앱 지원 종료에 따른 우리WON카드 지원을 추가했습니다.
119 | - L.PAY 및 L.POINT 앱 통합에 따른 L.POINT 지원을 추가했습니다.
120 | - 네이버페이 앱 로그인 기능을 추가했습니다.
121 | - webview_flutter 사용에 따른 웹뷰 충돌을 해결했습니다.
122 |
123 | ## [0.10.0-dev.1](https://github.com/portone-io/portone_flutter/tree/v0.10.0-dev.1)
124 | - [안드로이드] 외부 앱 로딩 방식을 변경했습니다.
125 | - flutter v2로의 마이그레이션 및 dart 버전 업그레이드에 따른 null safety에 대한 대응을 완료했습니다.
126 | - 네이버페이 및 체크아웃 결제용 파라미터를 추가했습니다.
127 | - 일부 pg 결제화면에서 javascript 팝업이 제대로 나타나지 않는 현상을 해결했습니다.
128 | - 결제 데이터를 json으로 변환하는 방식을 변경했습니다.
129 | - 페이나우 지원을 추가했습니다.
130 |
131 | ## [0.9.15](https://github.com/portone-io/portone_flutter/tree/v0.9.15)
132 | - [안드로이드] 코틀린 환경에서 registerWith 함수가 트리거되지 않는 상황을 대비해 ActivityAware 인터페이스를 implement 하도록 로직을 추가하였습니다.
133 |
134 | ## [0.9.14](https://github.com/portone-io/portone_flutter/tree/v0.9.14)
135 | - [안드로이드] API 30 지원을 위해 url_launcher의 canLaunch 코드를 제거하였습니다.
136 |
137 | ## [0.9.13](https://github.com/portone-io/portone_flutter/tree/v0.9.13)
138 | - [다날 - 휴대폰 소액결제] 주문명이 중복되는 것을 방지하기 위해 company 파라메터를 추가하였습니다.
139 |
140 | ## [0.9.12](https://github.com/portone-io/portone_flutter/tree/v0.9.12)
141 | - [안드로이드] KG이니시스 - 실시간계좌이체시 국민리브, NH앱캐시, NG상상뱅크, BNK경남은행 앱의 링크가 누락된 부분을 추가하였습니다.
142 | - uni_links 패키지의 버전을 0.4.0으로 올렸습니다.
143 |
144 | ## [0.9.11](https://github.com/portone-io/portone_flutter/tree/v0.9.11)
145 | - 스마일페이, 차이페이, 페이플, 알리페이 예제 코드를 추가하였습니다.
146 | - 다날 - 본인인증 방식을 리디렉션 방식으로 변경하였습니다.
147 |
148 | ## [0.9.10](https://github.com/portone-io/portone_flutter/tree/v0.9.10)
149 | - iamport javascript sdk 버전을 v1.1.8로 올렸습니다.
150 | - 안드로이드6에서 결제/본인인증 창 미렌더링 이슈를 해결하였습니다.
151 |
152 | ## [0.9.9](https://github.com/portone-io/portone_flutter/tree/v.0.9.9)
153 | - 농협 올원페이 앱 스킴 값 오타를 수정하였습니다.
154 | - build시 필요하지 않은 asset을 제거하기 위해 pubspec.yaml 파일을 수정하였습니다.
155 |
156 | ## [0.9.8](https://github.com/portone-io/portone_flutter/tree/v0.9.8)
157 | - 할부개월수 설정 파라미터 오타를 고쳤습니다.
158 |
159 | ## [0.9.6](https://github.com/portone-io/portone_flutter/tree/v0.9.6)
160 | - niceMobileV2를 true로 기본으로 적용하고 이와 관련된 실시간 계좌이체 대비 코드를 적용하였습니다.
161 |
162 | ## [0.9.5](https://github.com/portone-io/portone_flutter/tree/v0.9.5)
163 | - 이니시스, 나이스 그리고 다날 일반결제시 제공기간 표기를 위한 파라미터(period)를 추가하였습니다.
164 |
165 | ## [0.9.4](https://github.com/portone-io/portone_flutter/tree/v0.9.4)
166 | - 안드로이드에서 NH농협카드 일반결제시, intent uri를 파싱할때 발생하는 exception을 처리하였습니다.
167 |
168 | ## [0.9.3](https://github.com/portone-io/portone_flutter/tree/v0.9.3)
169 | - CupertinoNavigationBar 지원을 위한 로직을 추가하였습니다.
170 |
171 | ## [0.9.2](https://github.com/portone-io/portone_flutter/tree/v0.9.2)
172 | - 3rd-party 앱 URL scheme값에 하나멤버스를 추가하였습니다.
173 |
174 | ## [0.9.1](https://github.com/portone-io/portone_flutter/tree/v0.9.1)
175 | - Health suggestions을 적용하였습니다.
176 |
177 | ## [0.9.0](https://github.com/portone-io/portone_flutter/tree/v0.9.0)
178 | - IOS 및 안드로이드 플러터 프로젝트에서 아임포트 일반/정기결제 및 휴대폰 본인인증 연동 기능을 제공합니다.
179 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2019 © Siot Inc. All rights reserved.
2 |
3 | Permission is hereby granted, free of charge,
4 | to any person obtaining a copy of this software
5 | and associated documentation files (the "Software"),
6 | to deal in the Software without restriction,
7 | including without limitation the rights to use, copy,
8 | modify, merge, publish, distribute, sublicense,
9 | and/or sell copies of the Software,
10 | and to permit persons to whom the Software is furnished to do so,
11 | subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice
14 | shall be included in all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
20 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # portone_flutter
2 | [](https://pub.dev/packages/portone_flutter)
3 |
4 | ---
5 | 포트원 V1 플러터 모듈입니다.
6 |
7 | ## 목차
8 | - [버전정보](CHANGELOG.md)
9 | - [지원정보](SUPPORT.md)
10 | - 설치하기
11 | - 설정하기
12 | - 공통 사항
13 | - IOS 설정하기
14 | - Android 설정하기
15 | - [실시간 계좌이체 설정하기](example/manuals/TRANS.md)
16 | - [예제](example/README.md)
17 | - [콜백 함수 설정하기](example/manuals/CALLBACK.md)
18 |
19 | ## 버전정보
20 | 최신버전은 [v0.12.0](https://github.com/portone-io/portone_flutter/tree/main) 입니다. 버전 히스토리는 [버전정보](CHANGELOG.md)를 참고하세요.
21 |
22 | ## 지원정보
23 | 포트원 V1 플러터 모듈은 일반/정기결제 및 휴대폰 본인인증 기능을 지원합니다. 결제 모듈이 지원하는 PG사 및 결제수단에 대한 자세한 내용은 [지원정보](SUPPORT.md)를 참고해주세요.
24 |
25 | ## 설치하기
26 | `pubspec.yaml` 파일에 `portone_flutter` 모듈을 추가해 귀하의 프로젝트에 포트원 V1 플러터 모듈을 설치할 수 있습니다.
27 |
28 | ```
29 | dependencies:
30 | portone_flutter: ^0.12.0
31 | ```
32 |
33 | ## 설정하기
34 |
35 | ### IOS 설정하기
36 | IOS에서 포트원 V1 결제연동 모듈을 사용하기 위해서는 `info.plist` 파일에 아래 3가지 항목을 설정해주셔야 합니다. `[프로젝트 이름]/ios/Runner.xcworkspace` 파일을 열어 왼쪽 프로젝트 패널 > Runner > info.plist 파일을 클릭합니다.
37 |
38 | #### 1. App Scheme 등록
39 | 외부 결제 앱(예) 페이코, 신한 판 페이)에서 결제 후 돌아올 때 사용할 URL identifier를 설정해야합니다.
40 |
41 | 
42 |
43 | 1. `URL types` 속성을 추가합니다.
44 | 2. item `0`를 확장하여 `URL schemes` 속성을 추가합니다.
45 | 3. item `0`에 App URL Scheme 값(EX. example)을 작성합니다.
46 |
47 | ```html
48 | ...
49 | CFBundleURLTypes
50 |
51 |
52 | CFBundleTypeRole
53 | Editor
54 | CFBundleURLSchemes
55 |
56 |
57 | example
58 |
59 |
60 |
61 | ...
62 | ```
63 |
64 | #### 2. 외부 앱 리스트 등록
65 | 3rd party앱(예) 카드사 앱, 간편결제 앱 등)을 실행할 수 있도록 외부 앱 리스트를 등록해야합니다.
66 |
67 | 1. [LSApplicationQueriesSchemes](https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/LaunchServicesKeys.html#//apple_ref/doc/uid/TP40009250-SW14) 속성을 추가합니다.
68 | 2. 외부 앱 URL Scheme 값을 하나씩 추가합니다.
69 |
70 | ```html
71 | ...
72 | LSApplicationQueriesSchemes
73 |
74 | kftc-bankpay
75 | ispmobile
76 | itms-apps
77 | hdcardappcardansimclick
78 | smhyundaiansimclick
79 | shinhan-sr-ansimclick
80 | smshinhanansimclick
81 | kb-acp
82 | mpocket.online.ansimclick
83 | ansimclickscard
84 | ansimclickipcollect
85 | vguardstart
86 | samsungpay
87 | scardcertiapp
88 | lottesmartpay
89 | lotteappcard
90 | cloudpay
91 | nhappcardansimclick
92 | nonghyupcardansimclick
93 | citispay
94 | citicardappkr
95 | citimobileapp
96 | kakaotalk
97 | payco
98 | lpayapp
99 | hanamopmoasign
100 | wooripay
101 | nhallonepayansimclick
102 | hanawalletmembers
103 | chaipayment
104 | kb-auth
105 | hyundaicardappcardid
106 | com.wooricard.wcard
107 | lmslpay
108 | lguthepay-xpay
109 | liivbank
110 | supertoss
111 | newsmartpib
112 | naversearchthirdlogin
113 |
114 | ...
115 | ```
116 |
117 | #### 3. App Transport Security 설정
118 |
119 | 
120 |
121 | 1. `App Transport Security Settings` 속성에 [Allow Arbitrary Loads in Web Content](https://developer.apple.com/documentation/bundleresources/information_property_list/nsapptransportsecurity/nsallowsarbitraryloadsinwebcontent) 속성을 추가합니다.
122 | 2. 그 값을 `true`로 설정합니다.
123 |
124 | ```html
125 | ...
126 | NSAppTransportSecurity
127 |
128 |
129 | NSAllowsArbitraryLoadsInWebContent
130 |
131 | NSAllowsArbitraryLoads
132 |
133 |
134 | ...
135 | ```
136 |
137 | #### 4. PASS 앱 Universal Link 설정 (선택 사항)
138 | Push 알림 없이 PASS 앱(SKT PASS, KT 인증, U+인증)을 실행해 본인인증을 진행하기 위해서는 `mRedirectUrl` 파라미터 설정이 필요합니다.
139 |
140 | ##### 4.1 mRedirectUrl 설정
141 | 본인인증 요청 시 `CertificationData` 객체의 `mRedirectUrl` 파라미터에 `UrlData.redirectUrl` 값을 설정해 주세요. 다날의 경우, `mRedirectUrl` 설정 시 `carrier` 파라미터로 전달한 통신사 선택을 사용자가 변경할 수 없으므로 주의해주세요.
142 |
143 | ```dart
144 | import 'package:portone_flutter/model/certification_data.dart';
145 | import 'package:portone_flutter/model/url_data.dart'; // UrlData import 추가
146 |
147 | // ...
148 |
149 | CertificationData data = CertificationData(
150 | mRedirectUrl: UrlData.redirectUrl
151 | // ... 기타 본인인증 데이터
152 | );
153 |
154 | // ...
155 | ```
156 |
157 | ### Android 설정하기
158 | 안드로이드 API 레벨 30에서 특정 카드사로 결제 시도시 `net::ERR_CLEARTEXT_NOT_PERMITTED` 오류가 발생한다는 버그가 보고되었습니다. 이를 해결하기 위해서는 [AndroidManifest.xml](https://github.com/portone-io/portone_flutter/blob/develop/example/android/app/src/main/AndroidManifest.xml#L13) 파일에 아래와 같이 [usesclearTextTraffic](https://developer.android.com/guide/topics/manifest/application-element#usesCleartextTraffic) 속성을 `true`로 설정해주셔야 합니다.
159 |
160 | ```xml
161 |
162 | ...
163 |
167 |
168 |
169 | ```
170 |
171 | ### 실시간 계좌이체 설정하기
172 | 웹 표준 이니시스와 나이스 정보통신은 뱅크페이 앱을 통해 실시간 계좌이체를 진행합니다. 뱅크페이에서 결제 인증 후 본래의 앱으로 복귀 해 다음단계로 진행을 하려면 별도 설정이 요구됩니다. 자세한 내용은 [실시간 계좌이체 설정하기](example/manuals/TRANS.md)를 참고해주세요.
173 |
174 |
175 | ## 예제
176 | 포트원 V1 플러터 모듈로 아래와 같이 일반/정기결제 및 휴대폰 본인인증 기능을 구현할 수 있습니다. 보다 자세한 내용은 [예제](example/README.md)를 참고하세요.
177 |
178 | #### 일반/정기결제 예제
179 | ```dart
180 | import 'package:flutter/material.dart';
181 |
182 | /* 포트원 V1 결제 모듈을 불러옵니다. */
183 | import 'package:portone_flutter/iamport_payment.dart';
184 | /* 포트원 V1 결제 데이터 모델을 불러옵니다. */
185 | import 'package:portone_flutter/model/payment_data.dart';
186 |
187 | class Payment extends StatelessWidget {
188 | @override
189 | Widget build(BuildContext context) {
190 | return IamportPayment(
191 | appBar: new AppBar(
192 | title: new Text('포트원 V1 결제'),
193 | ),
194 | /* 웹뷰 로딩 컴포넌트 */
195 | initialChild: Container(
196 | child: Center(
197 | child: Column(
198 | mainAxisAlignment: MainAxisAlignment.center,
199 | children: [
200 | Image.asset('assets/images/iamport-logo.png'),
201 | Padding(padding: EdgeInsets.symmetric(vertical: 15)),
202 | Text('잠시만 기다려주세요...', style: TextStyle(fontSize: 20)),
203 | ],
204 | ),
205 | ),
206 | ),
207 | /* [필수입력] 가맹점 식별코드 */
208 | userCode: 'iamport',
209 | /* [필수입력] 결제 데이터 */
210 | data: PaymentData(
211 | pg: 'html5_inicis', // PG사
212 | payMethod: 'card', // 결제수단
213 | name: '포트원 V1 결제데이터 분석', // 주문명
214 | merchantUid: 'mid_${DateTime.now().millisecondsSinceEpoch}', // 주문번호
215 | amount: 39000, // 결제금액
216 | buyerName: '홍길동', // 구매자 이름
217 | buyerTel: '01012345678', // 구매자 연락처
218 | buyerEmail: 'example@naver.com', // 구매자 이메일
219 | buyerAddr: '서울시 강남구 신사동 661-16', // 구매자 주소
220 | buyerPostcode: '06018', // 구매자 우편번호
221 | appScheme: 'example', // 앱 URL scheme
222 | cardQuota : [2,3] //결제창 UI 내 할부개월수 제한
223 | ),
224 | /* [필수입력] 콜백 함수 */
225 | callback: (Map result) {
226 | Navigator.pushReplacementNamed(
227 | context,
228 | '/result',
229 | arguments: result,
230 | );
231 | },
232 | );
233 | }
234 | }
235 | ```
236 |
237 |
238 | #### 본인인증 예제
239 |
240 | 이니시스 통합인증의 경우 다날 휴대폰 본인인증과 달리 `mRedirectUrl` 파라미터를 필수로 설정해주셔야 합니다.
241 |
242 | ##### 다날 휴대폰 본인인증
243 | ```dart
244 | import 'package:flutter/material.dart';
245 |
246 | /* 포트원 V1 휴대폰 본인인증 모듈을 불러옵니다. */
247 | import 'package:portone_flutter/iamport_certification.dart';
248 | /* 포트원 V1 휴대폰 본인인증 데이터 모델을 불러옵니다. */
249 | import 'package:portone_flutter/model/certification_data.dart';
250 |
251 | class Certification extends StatelessWidget {
252 | @override
253 | Widget build(BuildContext context) {
254 | return IamportCertification(
255 | appBar: new AppBar(
256 | title: new Text('포트원 V1 본인인증'),
257 | ),
258 | /* 웹뷰 로딩 컴포넌트 */
259 | initialChild: Container(
260 | child: Center(
261 | child: Column(
262 | mainAxisAlignment: MainAxisAlignment.center,
263 | children: [
264 | Image.asset('assets/images/iamport-logo.png'),
265 | Padding(padding: EdgeInsets.symmetric(vertical: 15)),
266 | Text('잠시만 기다려주세요...', style: TextStyle(fontSize: 20)),
267 | ],
268 | ),
269 | ),
270 | ),
271 | /* [필수입력] 가맹점 식별코드 */
272 | userCode: 'iamport',
273 | /* [필수입력] 본인인증 데이터 */
274 | data: CertificationData(
275 | pg: 'danal', // PG사
276 | merchantUid: 'mid_${DateTime.now().millisecondsSinceEpoch}', // 주문번호
277 | company: '포트원 V1', // 회사명 또는 URL
278 | carrier: 'SKT', // 통신사
279 | name: '홍길동', // 이름
280 | phone: '01012341234', // 전화번호
281 | ),
282 | /* [필수입력] 콜백 함수 */
283 | callback: (Map result) {
284 | Navigator.pushReplacementNamed(
285 | context,
286 | '/result',
287 | arguments: result,
288 | );
289 | },
290 | );
291 | }
292 | }
293 | ```
294 | ##### 이니시스 통합인증
295 | ```dart
296 | import 'package:flutter/material.dart';
297 |
298 | /* 포트원 V1 휴대폰 본인인증 모듈을 불러옵니다. */
299 | import 'package:portone_flutter/iamport_certification.dart';
300 | /* 포트원 V1 휴대폰 본인인증 데이터 모델을 불러옵니다. */
301 | import 'package:portone_flutter/model/certification_data.dart';
302 |
303 | class Certification extends StatelessWidget {
304 | @override
305 | Widget build(BuildContext context) {
306 | return IamportCertification(
307 | appBar: new AppBar(
308 | title: new Text('포트원 V1 본인인증'),
309 | ),
310 | /* 웹뷰 로딩 컴포넌트 */
311 | initialChild: Container(
312 | child: Center(
313 | child: Column(
314 | mainAxisAlignment: MainAxisAlignment.center,
315 | children: [
316 | Image.asset('assets/images/iamport-logo.png'),
317 | Padding(padding: EdgeInsets.symmetric(vertical: 15)),
318 | Text('잠시만 기다려주세요...', style: TextStyle(fontSize: 20)),
319 | ],
320 | ),
321 | ),
322 | ),
323 | /* [필수입력] 가맹점 식별코드 */
324 | userCode: 'iamport',
325 | /* [필수입력] 본인인증 데이터 */
326 | data: CertificationData(
327 | pg: 'inicis_unified', // PG사
328 | merchantUid: 'mid_${DateTime.now().millisecondsSinceEpoch}', // 주문번호
329 | mRedirectUrl: 'https://example.com', // 본인인증 후 이동할 URL
330 | ),
331 | /* [필수입력] 콜백 함수 */
332 | callback: (Map result) {
333 | Navigator.pushReplacementNamed(
334 | context,
335 | '/result',
336 | arguments: result,
337 | );
338 | },
339 | );
340 | }
341 | }
342 | ```
343 |
344 | ## 콜백 함수 설정하기
345 | 콜백 함수는 필수입력 필드로, 결제/본인인증 완료 후 라우트 이동을 위해 아래와 같이 로직을 작성할 수 있습니다. 콜백 함수에 대한 자세한 설명은 [콜백 설정하기](example/manuals/CALLBACK.md)를 참고하세요.
346 |
347 | ```dart
348 | ...
349 | callback: (Map result) {
350 | Navigator.pushReplacementNamed(
351 | context,
352 | '/result',
353 | arguments: result,
354 | );
355 | },
356 | ...
357 | ```
358 |
--------------------------------------------------------------------------------
/SUPPORT.md:
--------------------------------------------------------------------------------
1 | # 지원정보
2 |
3 | 포트원 V1 플러터 모듈이 지원하는 PG사 및 결제수단 안내입니다.
4 |
5 | | pg | 이름 | 결제수단 |
6 | |--------------|---------------------|-----------------------------------------------------------------------------------------------|
7 | | html5_inicis | 웹 표준 이니시스 | 신용카드, 가상계좌, 실시간 계좌이체(IOS 별도 설정 필요), 휴대폰 소액결제, 삼성페이, KPAY, 문화상품권, 스마트문상, 해피머니 |
8 | | kcp | NHN KCP | 신용카드, 가상계좌, 실시간 계좌이체, 휴대폰 소액결제, 삼성페이 |
9 | | kcp_billing | NHN KCP 정기결제 | 신용카드 |
10 | | uplus | 토스페이먼츠 - (구)LG 유플러스 | 신용카드, 가상계좌, 실시간 계좌이체, 휴대폰 소액결제, 문화상품권, 스마트문상, 도서상푼권 |
11 | | jtnet | JTNET | 신용카드, 가상계좌, 실시간 계좌이체, 휴대폰 소액결제 |
12 | | nice | 나이스 정보통신 | 신용카드, 가상계좌, 실시간 계좌이체(IOS/안드로이드 별도 설정 필요), 휴대폰 소액결제 |
13 | | kakaopay | 신 - 카카오페이 | 신용카드 |
14 | | kakao | 구 - LG CNS 카카오페이 | 신용카드 |
15 | | danal | 다날 휴대폰 소액결제 | 휴대폰 소액결제 |
16 | | danal_tpay | 다날 일반결제 | 신용카드, 가상계좌, 실시간 계좌이체 |
17 | | kicc | 한국정보통신 | 신용카드, 가상계좌, 실시간 계좌이체, 휴대폰 소액결제 |
18 | | paypal | 페이팔 | 신용카드 |
19 | | mobilians | 모빌리언스 | 신용카드, 가상계좌(준비중), 실시간 계좌이체(준비중), 휴대폰 소액결제 |
20 | | payco | 페이코 | 신용카드 |
21 | | eximbay | 엑심베이 | 신용카드 [주의사항](https://github.com/iamport/iamport-react-native/issues/70#issuecomment-704601908) |
22 | | settle | 세틀뱅크 | 가상계좌 |
23 | | naverco | 네이버 체크아웃 | 신용카드, 가상계좌, 실시간 계좌이체, 휴대폰 소액결제 |
24 | | naverpay | 네이버페이 | 신용카드, 가상계좌, 실시간 계좌이체, 휴대폰 소액결제 |
25 | | smilepay | 스마일페이 | 신용카드 [주의사항](https://github.com/iamport/iamport-react-native/issues/71) |
26 | | chai | 차이페이 | 신용카드 |
27 | | payple | 페이플 | 실시간 계좌이체 |
28 | | alipay | 알리페이 | 신용카드 |
29 | | bluewalnut | 블루월넛 | 신용카드, 가상계좌, 실시간 계좌이체, 휴대폰 소액결제 |
30 | | tosspay | 토스 - 간편결제 | 신용카드 |
31 | | smartro | 스마트로 | 신용카드, 가상계좌, 실시간 계좌이체 |
32 | | tosspayments | 토스페이먼츠 | 신용카드 |
33 | | welcome | 웰컴페이먼츠 | 신용카드, 가상계좌, 실시간 계좌이체, 휴대폰 소액결제, 문화상품권 |
34 | | tosspay_v2 | 토스페이(V2) | 신용카드, 실시간 계좌이체 |
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | group 'io.portone.portone_flutter'
2 | version '1.0-SNAPSHOT'
3 |
4 | buildscript {
5 | ext.kotlin_version = '1.9.23'
6 | repositories {
7 | google()
8 | mavenCentral()
9 | }
10 |
11 | dependencies {
12 | classpath 'com.android.tools.build:gradle:8.4.2'
13 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
14 | }
15 | }
16 |
17 | rootProject.allprojects {
18 | repositories {
19 | google()
20 | mavenCentral()
21 | }
22 | }
23 |
24 | apply plugin: 'com.android.library'
25 | apply plugin: 'kotlin-android'
26 |
27 | android {
28 | compileSdk 34
29 |
30 | namespace = 'io.portone.portone_flutter'
31 |
32 | sourceSets {
33 | main.java.srcDirs += 'src/main/kotlin'
34 | }
35 | defaultConfig {
36 | minSdkVersion 23
37 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
38 | }
39 | lintOptions {
40 | disable 'InvalidPackage'
41 | }
42 |
43 | dependencies {
44 | implementation 'androidx.annotation:annotation:1.8.0'
45 | }
46 |
47 | compileOptions {
48 | sourceCompatibility JavaVersion.VERSION_17
49 | targetCompatibility JavaVersion.VERSION_17
50 | }
51 |
52 | kotlinOptions {
53 | jvmTarget = "17"
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1024M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Jun 12 17:20:40 KST 2024
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'portone_flutter'
2 |
--------------------------------------------------------------------------------
/android/src/main/kotlin/io/portone/portone_flutter/IamportFlutterPlugin.kt:
--------------------------------------------------------------------------------
1 | package io.portone.portone_flutter
2 |
3 | import androidx.annotation.NonNull
4 |
5 | import io.flutter.embedding.engine.plugins.FlutterPlugin
6 | import io.flutter.plugin.common.MethodCall
7 | import io.flutter.plugin.common.MethodChannel
8 | import io.flutter.plugin.common.MethodChannel.MethodCallHandler
9 | import io.flutter.plugin.common.MethodChannel.Result
10 |
11 | /** IamportFlutterPlugin */
12 | class IamportFlutterPlugin: FlutterPlugin, MethodCallHandler {
13 | /// The MethodChannel that will the communication between Flutter and native Android
14 | ///
15 | /// This local reference serves to register the plugin with the Flutter Engine and unregister it
16 | /// when the Flutter Engine is detached from the Activity
17 | private lateinit var channel : MethodChannel
18 |
19 | override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
20 | channel = MethodChannel(flutterPluginBinding.binaryMessenger, "portone_flutter")
21 | channel.setMethodCallHandler(this)
22 | }
23 |
24 | override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
25 | if (call.method == "getPlatformVersion") {
26 | result.success("Android ${android.os.Build.VERSION.RELEASE}")
27 | } else {
28 | result.notImplemented()
29 | }
30 | }
31 |
32 | override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
33 | channel.setMethodCallHandler(null)
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/assets/images/allow-arbitrary.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/portone-io/portone_flutter/4a8c02372ab1a53fb52a4485f8c730bb138c3a2c/assets/images/allow-arbitrary.gif
--------------------------------------------------------------------------------
/assets/images/app-scheme-registry.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/portone-io/portone_flutter/4a8c02372ab1a53fb52a4485f8c730bb138c3a2c/assets/images/app-scheme-registry.gif
--------------------------------------------------------------------------------
/assets/images/iamport-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/portone-io/portone_flutter/4a8c02372ab1a53fb52a4485f8c730bb138c3a2c/assets/images/iamport-logo.png
--------------------------------------------------------------------------------
/build.yaml:
--------------------------------------------------------------------------------
1 | targets:
2 | $default:
3 | builders:
4 | json_serializable:
5 | options:
6 | include_if_null: false
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # Visual Studio Code related
19 | .vscode/
20 |
21 | # Flutter/Dart/Pub related
22 | **/doc/api/
23 | .dart_tool/
24 | .flutter-plugins
25 | .flutter-plugins-dependencies
26 | .packages
27 | .pub-cache/
28 | .pub/
29 | /build/
30 | .pubspec.lock
31 | lib/main.mapper.g.dart
32 |
33 | # Android related
34 | **/android/**/gradle-wrapper.jar
35 | **/android/.gradle
36 | **/android/captures/
37 | **/android/gradlew
38 | **/android/gradlew.bat
39 | **/android/local.properties
40 | **/android/**/GeneratedPluginRegistrant.java
41 |
42 | # iOS/XCode related
43 | **/ios/**/*.mode1v3
44 | **/ios/**/*.mode2v3
45 | **/ios/**/*.moved-aside
46 | **/ios/**/*.pbxuser
47 | **/ios/**/*.perspectivev3
48 | **/ios/**/*sync/
49 | **/ios/**/.sconsign.dblite
50 | **/ios/**/.tags*
51 | **/ios/**/.vagrant/
52 | **/ios/**/DerivedData/
53 | **/ios/**/Icon?
54 | **/ios/**/Pods/
55 | **/ios/**/.symlinks/
56 | **/ios/**/profile
57 | **/ios/**/xcuserdata
58 | **/ios/.generated/
59 | **/ios/Flutter/App.framework
60 | **/ios/Flutter/Flutter.framework
61 | **/ios/Flutter/Generated.xcconfig
62 | **/ios/Flutter/app.flx
63 | **/ios/Flutter/app.zip
64 | **/ios/Flutter/flutter_assets/
65 | **/ios/ServiceDefinitions.json
66 | **/ios/Runner/GeneratedPluginRegistrant.*
67 |
68 | # Exceptions to above rules.
69 | !**/ios/**/default.mode1v3
70 | !**/ios/**/default.mode2v3
71 | !**/ios/**/default.pbxuser
72 | !**/ios/**/default.perspectivev3
73 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
74 |
--------------------------------------------------------------------------------
/example/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: 7a4c33425ddd78c54aba07d86f3f9a4a0051769b
8 | channel: stable
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | # portone_flutter_example
2 |
3 | 포트원 V1 플러터 모듈 예제 안내입니다.
4 |
5 | ## 일반/정기결제 예제
6 | ```dart
7 | import 'package:flutter/material.dart';
8 |
9 | /* 포트원 V1 결제 모듈을 불러옵니다. */
10 | import 'package:portone_flutter/iamport_payment.dart';
11 | /* 포트원 V1 결제 데이터 모델을 불러옵니다. */
12 | import 'package:portone_flutter/model/payment_data.dart';
13 |
14 | class Payment extends StatelessWidget {
15 | @override
16 | Widget build(BuildContext context) {
17 | return IamportPayment(
18 | appBar: new AppBar(
19 | title: new Text('포트원 V1 결제'),
20 | ),
21 | /* 웹뷰 로딩 컴포넌트 */
22 | initialChild: Container(
23 | child: Center(
24 | child: Column(
25 | mainAxisAlignment: MainAxisAlignment.center,
26 | children: [
27 | Image.asset('assets/images/iamport-logo.png'),
28 | Container(
29 | padding: EdgeInsets.fromLTRB(0.0, 30.0, 0.0, 0.0),
30 | child: Text('잠시만 기다려주세요...', style: TextStyle(fontSize: 20.0)),
31 | ),
32 | ],
33 | ),
34 | ),
35 | ),
36 | /* [필수입력] 가맹점 식별코드 */
37 | userCode: 'iamport',
38 | /* [필수입력] 결제 데이터 */
39 | data: PaymentData({
40 | pg: 'html5_inicis', // PG사
41 | payMethod: 'card', // 결제수단
42 | name: '포트원 V1 결제데이터 분석', // 주문명
43 | merchantUid: 'mid_${DateTime.now().millisecondsSinceEpoch}', // 주문번호
44 | amount: 39000, // 결제금액
45 | buyerName: '홍길동', // 구매자 이름
46 | buyerTel: '01012345678', // 구매자 연락처
47 | buyerEmail: 'example@naver.com', // 구매자 이메일
48 | buyerAddr: '서울시 강남구 신사동 661-16', // 구매자 주소
49 | buyerPostcode: '06018', // 구매자 우편번호
50 | appScheme: 'example', // 앱 URL scheme
51 | displayCardQuota: [2, 3] // 결제창 UI 내 할부개월수 제한
52 | }),
53 | /* [필수입력] 콜백 함수 */
54 | callback: (Map result) {
55 | Navigator.pushReplacementNamed(
56 | context,
57 | '/payment-result',
58 | arguments: result,
59 | );
60 | },
61 | );
62 | }
63 | }
64 | ```
65 |
66 | ### IamportPayment 모델
67 |
68 | | Prop | Type | Description | Required |
69 | | ---------------- | ------------------- | -------------------- | ---------- |
70 | | appBar | PreferredSizeWidget | 앱 네비게이션 헤더 바 | false |
71 | | userCode | String | 가맹점 식별코드 | true |
72 | | initialChild | 플러터 컴포넌트 | 웹뷰 로드시 보여질 컴포넌트 | false |
73 | | data | `PaymentData` | 결제에 필요한 정보 | true |
74 | | callback | function | 결제 후 실행 될 함수 | true |
75 |
76 | ### PaymentData 모델 [자세히보기](https://docs.iamport.kr/sdk/javascript-sdk#request_pay)
77 |
78 | | Key | Type | Description | Required |
79 | | -------------- | ------------------------ | -------------- | ------------------------ |
80 | | pg | String | PG사 | false |
81 | | payMethod | String | 결제수단 | false |
82 | | display | `Map>` | 할부개월수 | false |
83 | | vbankDue | String | 가상계좌 입금기한 | false (가상계좌시 필수) |
84 | | bizNum | String | 사업자번호 | false (다날 - 가상계좌시 필수) |
85 | | digital | bool | 실물컨텐츠 여부 | false (휴대폰 소액결제시 필수) |
86 | | escrow | bool | 에스크로 여부 | false |
87 | | name | String | 주문명 | true |
88 | | amount | int | 결제금액 | true |
89 | | currency | String | 화폐 단위 | false |
90 | | customData | Map | 임의 지정 데이터 | false |
91 | | taxFree | int | 면세 공급 가액 | false |
92 | | vat | int | 부가세 | false |
93 | | language | String | 결제 창 언어설정 | false |
94 | | merchantUid | String | 주문번호 | true |
95 | | buyerName | String | 구매자 이름 | false |
96 | | buyerTel | String | 구매자 연락처 | false |
97 | | buyerEmail | String | 구매자 이메일 | false |
98 | | buyerAddr | String | 구매자 주소 | false |
99 | | buyerPostcode | String | 구매자 우편번호 | false |
100 | | noticeUrl | String | 웹훅 URL | false |
101 | | customerUid | String | 정기결제 카드정보 | false (정기결제시 필수) |
102 | | appScheme | String | 앱 스킴 | true |
103 | | popup | bool | 페이팔 팝업 여부 | false |
104 | | naverPopupMode | bool | 네이버페이 팝업 여부 | false |
105 | | `period` | Map | 다날 - 신용카드/계좌이체/가상계좌 전용 제공기간 | false |
106 | | `company` | String | 다날 - 휴대폰 소액결제 전용 주문명 앞 괄호 안 텍스트 | false |
107 |
108 | ### period
109 | 이니시스, 나이스 그리고 다날 신용카드/계좌이체/가상계좌 결제시 제공기간 표기를 위한 파라미터입니다. 제공기간 시작 날짜(`from`)와 끝 날짜(`to`)를 아래와 같이 `YYYYMMDD` 형태로 넘겨주세요.
110 |
111 | | key | Type | Description |
112 | | ---- | ---------------- | ------------- |
113 | | from | String(YYYYMMDD) | 제공기간 시작 날짜 |
114 | | to | String(YYYYMMDD) | 제공기간 종료날짜 |
115 |
116 | ```dart
117 | data: PaymentData.fromJson({
118 | ...
119 | period: { // 제공기간 2020년 1월 1일 ~ 2020년 12월 31일
120 | from: '20200101', // 제공기간 시작 날짜
121 | to: '20201231', // 제공기간 종료 날짜
122 | },
123 | }),
124 | ```
125 |
126 | ### company
127 | 다날 휴대폰 소액결제시 주문명 앞 괄호 안 텍스트에 표기될 회사명을 위한 파라미터입니다. 결제창 내 주문명은 `(회사명) 주문명`과 같이 표기되며, 누락시 `(주문명) 주문명`과 같이 표기됩니다.
128 |
129 | ## 휴대폰 본인인증 예제
130 | ```dart
131 | import 'package:flutter/material.dart';
132 |
133 | /* 포트원 V1 휴대폰 본인인증 모듈을 불러옵니다. */
134 | import 'package:portone_flutter/iamport_certification.dart';
135 | /* 포트원 V1 휴대폰 본인인증 데이터 모델을 불러옵니다. */
136 | import 'package:portone_flutter/model/certification_data.dart';
137 |
138 | class Certification extends StatelessWidget {
139 | @override
140 | Widget build(BuildContext context) {
141 | return IamportCertification(
142 | appBar: new AppBar(
143 | title: new Text('포트원 V1 본인인증'),
144 | ),
145 | /* 웹뷰 로딩 컴포넌트 */
146 | initialChild: Container(
147 | child: Center(
148 | child: Column(
149 | mainAxisAlignment: MainAxisAlignment.center,
150 | children: [
151 | Image.asset('assets/images/iamport-logo.png'),
152 | Container(
153 | padding: EdgeInsets.fromLTRB(0.0, 30.0, 0.0, 0.0),
154 | child: Text('잠시만 기다려주세요...', style: TextStyle(fontSize: 20.0)),
155 | ),
156 | ],
157 | ),
158 | ),
159 | ),
160 | /* [필수입력] 가맹점 식별코드 */
161 | userCode: 'iamport',
162 | /* [필수입력] 본인인증 데이터 */
163 | data: CertificationData({
164 | merchantUid: 'mid_${DateTime.now().millisecondsSinceEpoch}', // 주문번호
165 | company: '포트원 V1', // 회사명 또는 URL
166 | carrier: 'SKT', // 통신사
167 | name: '홍길동', // 이름
168 | phone: '01012341234', // 전화번호
169 | }),
170 | /* [필수입력] 콜백 함수 */
171 | callback: (Map result) {
172 | Navigator.pushReplacementNamed(
173 | context,
174 | '/certification-result',
175 | arguments: result,
176 | );
177 | },
178 | );
179 | }
180 | }
181 | ```
182 |
183 | ### IamportCertification 모델
184 |
185 | | Prop | Type | Description | Required |
186 | | ------------- | ------------------- | -------------------- | ---------- |
187 | | appBar | PreferredSizeWidget | 앱 네비게이션 헤더 바 | false |
188 | | userCode | string | 가맹점 식별코드 | true |
189 | | initialChild | 플러터 컴포넌트 | 웹뷰 로드시 보여질 컴포넌트 | false |
190 | | data | `CertificationData` | 본인인증에 필요한 정보 | true |
191 | | callback | function | 본인인증 후 실행 될 함수 | true |
192 |
193 | ### CertificationData 모델 [자세히보기](https://docs.iamport.kr/sdk/javascript-sdk#certification)
194 |
195 | | Key | Type | Description | Required |
196 | | ------------ | ------ | --------------- | -------- |
197 | | merchantUid | String | 주문번호 | true |
198 | | company | String | 회사명 또는 URL | false |
199 | | carrier | String | 통신사 | false |
200 | | | | - `SKT`: SKT | |
201 | | | | - `KTF`: KT | |
202 | | | | - `LGT`: LGU+ | |
203 | | | | - `MVNO`: 알뜰폰 | |
204 | | name | String | 본인인증 할 이름 | false |
205 | | phone | String | 본인인증 할 전화번호 | false |
206 | | minAge | int | 허용 최소 만 나이 | false |
207 |
--------------------------------------------------------------------------------
/example/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
9 | # Remember to never publicly share your keystore.
10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
11 | key.properties
12 |
--------------------------------------------------------------------------------
/example/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.android.application")
3 | id("kotlin-android")
4 | // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
5 | id("dev.flutter.flutter-gradle-plugin")
6 | }
7 |
8 | android {
9 | namespace = 'io.portone.portone_flutter_example'
10 | compileSdk = flutter.compileSdkVersion
11 |
12 | sourceSets {
13 | main.java.srcDirs += 'src/main/kotlin'
14 | }
15 |
16 | defaultConfig {
17 | applicationId "io.portone.portone_flutter_example"
18 | minSdk = 23
19 | targetSdk = flutter.targetSdkVersion
20 | versionCode = flutter.versionCode
21 | versionName = flutter.versionName
22 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
23 | }
24 |
25 | buildTypes {
26 | release {
27 | // TODO: Add your own signing config for the release build.
28 | // Signing with the debug keys for now, so `flutter run --release` works.
29 | signingConfig = signingConfigs.getByName("debug")
30 | }
31 | }
32 | lint {
33 | disable 'InvalidPackage'
34 | }
35 |
36 | compileOptions {
37 | sourceCompatibility JavaVersion.VERSION_17
38 | targetCompatibility JavaVersion.VERSION_17
39 | }
40 |
41 | kotlinOptions {
42 | jvmTarget = JavaVersion.VERSION_17.toString()
43 | }
44 | }
45 |
46 | flutter {
47 | source '../..'
48 | }
49 |
50 | dependencies {
51 | testImplementation 'junit:junit:4.13.2'
52 | androidTestImplementation 'androidx.test:runner:1.5.2'
53 | }
54 |
--------------------------------------------------------------------------------
/example/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
13 |
16 |
24 |
28 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/example/android/app/src/main/kotlin/io/portone/example/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package io.portone.example
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity()
6 |
--------------------------------------------------------------------------------
/example/android/app/src/main/kotlin/io/portone/portone_flutter_example/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package io.portone.portone_flutter_example
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity() {
6 | }
7 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable-v21/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/portone-io/portone_flutter/4a8c02372ab1a53fb52a4485f8c730bb138c3a2c/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/portone-io/portone_flutter/4a8c02372ab1a53fb52a4485f8c730bb138c3a2c/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/portone-io/portone_flutter/4a8c02372ab1a53fb52a4485f8c730bb138c3a2c/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/portone-io/portone_flutter/4a8c02372ab1a53fb52a4485f8c730bb138c3a2c/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/portone-io/portone_flutter/4a8c02372ab1a53fb52a4485f8c730bb138c3a2c/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
--------------------------------------------------------------------------------
/example/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/example/android/build.gradle.kts:
--------------------------------------------------------------------------------
1 | allprojects {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | }
6 | }
7 |
8 | val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get()
9 | rootProject.layout.buildDirectory.value(newBuildDir)
10 |
11 | subprojects {
12 | val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
13 | project.layout.buildDirectory.value(newSubprojectBuildDir)
14 | }
15 | subprojects {
16 | project.evaluationDependsOn(":app")
17 | }
18 |
19 | tasks.register("clean") {
20 | delete(rootProject.layout.buildDirectory)
21 | }
22 |
--------------------------------------------------------------------------------
/example/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1024M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 | android.nonTransitiveRClass=false
5 | android.nonFinalResIds=false
--------------------------------------------------------------------------------
/example/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 08:50:38 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-all.zip
7 |
--------------------------------------------------------------------------------
/example/android/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | val flutterSdkPath = run {
3 | val properties = java.util.Properties()
4 | file("local.properties").inputStream().use { properties.load(it) }
5 | val flutterSdkPath = properties.getProperty("flutter.sdk")
6 | require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
7 | flutterSdkPath
8 | }
9 |
10 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
11 |
12 | repositories {
13 | google()
14 | mavenCentral()
15 | gradlePluginPortal()
16 | }
17 | }
18 |
19 | plugins {
20 | id("dev.flutter.flutter-plugin-loader") version "1.0.0"
21 | id("com.android.application") version "8.7.2" apply false
22 | id("org.jetbrains.kotlin.android") version "1.9.23" apply false
23 | }
24 |
25 | include(":app")
--------------------------------------------------------------------------------
/example/assets/images/iamport-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/portone-io/portone_flutter/4a8c02372ab1a53fb52a4485f8c730bb138c3a2c/example/assets/images/iamport-logo.png
--------------------------------------------------------------------------------
/example/ios/.gitignore:
--------------------------------------------------------------------------------
1 | *.mode1v3
2 | *.mode2v3
3 | *.moved-aside
4 | *.pbxuser
5 | *.perspectivev3
6 | **/*sync/
7 | .sconsign.dblite
8 | .tags*
9 | **/.vagrant/
10 | **/DerivedData/
11 | Icon?
12 | **/Pods/
13 | **/.symlinks/
14 | profile
15 | xcuserdata
16 | **/.generated/
17 | Flutter/App.framework
18 | Flutter/Flutter.framework
19 | Flutter/Flutter.podspec
20 | Flutter/Generated.xcconfig
21 | Flutter/ephemeral/
22 | Flutter/app.flx
23 | Flutter/app.zip
24 | Flutter/flutter_assets/
25 | Flutter/flutter_export_environment.sh
26 | ServiceDefinitions.json
27 | Runner/GeneratedPluginRegistrant.*
28 |
29 | # Exceptions to above rules.
30 | !default.mode1v3
31 | !default.mode2v3
32 | !default.pbxuser
33 | !default.perspectivev3
34 |
--------------------------------------------------------------------------------
/example/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 11.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/example/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/example/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/example/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment this line to define a global platform for your project
2 | # platform :ios, '11.0'
3 |
4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
6 |
7 | project 'Runner', {
8 | 'Debug' => :debug,
9 | 'Profile' => :release,
10 | 'Release' => :release,
11 | }
12 |
13 | def flutter_root
14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
15 | unless File.exist?(generated_xcode_build_settings_path)
16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
17 | end
18 |
19 | File.foreach(generated_xcode_build_settings_path) do |line|
20 | matches = line.match(/FLUTTER_ROOT\=(.*)/)
21 | return matches[1].strip if matches
22 | end
23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
24 | end
25 |
26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
27 |
28 | flutter_ios_podfile_setup
29 |
30 | target 'Runner' do
31 | use_frameworks!
32 | use_modular_headers!
33 |
34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
35 | end
36 |
37 | post_install do |installer|
38 | installer.pods_project.targets.each do |target|
39 | flutter_additional_ios_build_settings(target)
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
33 |
34 |
40 |
41 |
42 |
43 |
44 |
45 |
56 |
58 |
64 |
65 |
66 |
67 |
68 |
69 |
75 |
77 |
83 |
84 |
85 |
86 |
88 |
89 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/ios/Runner/AppDelegate.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | @interface AppDelegate : FlutterAppDelegate
5 |
6 | @end
7 |
--------------------------------------------------------------------------------
/example/ios/Runner/AppDelegate.m:
--------------------------------------------------------------------------------
1 | #include "AppDelegate.h"
2 | #include "GeneratedPluginRegistrant.h"
3 |
4 | @implementation AppDelegate
5 |
6 | - (BOOL)application:(UIApplication *)application
7 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
8 | [GeneratedPluginRegistrant registerWithRegistry:self];
9 | // Override point for customization after application launch.
10 | return [super application:application didFinishLaunchingWithOptions:launchOptions];
11 | }
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/example/ios/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Flutter
3 |
4 | @UIApplicationMain
5 | @objc class AppDelegate: FlutterAppDelegate {
6 | override func application(
7 | _ application: UIApplication,
8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
9 | ) -> Bool {
10 | GeneratedPluginRegistrant.register(with: self)
11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-App-20x20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-App-20x20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-App-29x29@1x.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-App-29x29@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-App-29x29@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-App-40x40@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-App-40x40@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "Icon-App-60x60@2x.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "60x60",
53 | "idiom" : "iphone",
54 | "filename" : "Icon-App-60x60@3x.png",
55 | "scale" : "3x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "Icon-App-20x20@1x.png",
61 | "scale" : "1x"
62 | },
63 | {
64 | "size" : "20x20",
65 | "idiom" : "ipad",
66 | "filename" : "Icon-App-20x20@2x.png",
67 | "scale" : "2x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-App-29x29@1x.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "29x29",
77 | "idiom" : "ipad",
78 | "filename" : "Icon-App-29x29@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "Icon-App-40x40@1x.png",
85 | "scale" : "1x"
86 | },
87 | {
88 | "size" : "40x40",
89 | "idiom" : "ipad",
90 | "filename" : "Icon-App-40x40@2x.png",
91 | "scale" : "2x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "Icon-App-76x76@1x.png",
97 | "scale" : "1x"
98 | },
99 | {
100 | "size" : "76x76",
101 | "idiom" : "ipad",
102 | "filename" : "Icon-App-76x76@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "83.5x83.5",
107 | "idiom" : "ipad",
108 | "filename" : "Icon-App-83.5x83.5@2x.png",
109 | "scale" : "2x"
110 | },
111 | {
112 | "size" : "1024x1024",
113 | "idiom" : "ios-marketing",
114 | "filename" : "Icon-App-1024x1024@1x.png",
115 | "scale" : "1x"
116 | }
117 | ],
118 | "info" : {
119 | "version" : 1,
120 | "author" : "xcode"
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/portone-io/portone_flutter/4a8c02372ab1a53fb52a4485f8c730bb138c3a2c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/portone-io/portone_flutter/4a8c02372ab1a53fb52a4485f8c730bb138c3a2c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/portone-io/portone_flutter/4a8c02372ab1a53fb52a4485f8c730bb138c3a2c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/portone-io/portone_flutter/4a8c02372ab1a53fb52a4485f8c730bb138c3a2c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/portone-io/portone_flutter/4a8c02372ab1a53fb52a4485f8c730bb138c3a2c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/portone-io/portone_flutter/4a8c02372ab1a53fb52a4485f8c730bb138c3a2c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/portone-io/portone_flutter/4a8c02372ab1a53fb52a4485f8c730bb138c3a2c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/portone-io/portone_flutter/4a8c02372ab1a53fb52a4485f8c730bb138c3a2c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/portone-io/portone_flutter/4a8c02372ab1a53fb52a4485f8c730bb138c3a2c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/portone-io/portone_flutter/4a8c02372ab1a53fb52a4485f8c730bb138c3a2c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/portone-io/portone_flutter/4a8c02372ab1a53fb52a4485f8c730bb138c3a2c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/portone-io/portone_flutter/4a8c02372ab1a53fb52a4485f8c730bb138c3a2c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/portone-io/portone_flutter/4a8c02372ab1a53fb52a4485f8c730bb138c3a2c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/portone-io/portone_flutter/4a8c02372ab1a53fb52a4485f8c730bb138c3a2c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/portone-io/portone_flutter/4a8c02372ab1a53fb52a4485f8c730bb138c3a2c/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LaunchImage.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LaunchImage@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "LaunchImage@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/portone-io/portone_flutter/4a8c02372ab1a53fb52a4485f8c730bb138c3a2c/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/portone-io/portone_flutter/4a8c02372ab1a53fb52a4485f8c730bb138c3a2c/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/portone-io/portone_flutter/4a8c02372ab1a53fb52a4485f8c730bb138c3a2c/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/example/ios/Runner/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/example/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/example/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | portone_flutter_example
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | $(FLUTTER_BUILD_NAME)
19 | CFBundleSignature
20 | ????
21 | CFBundleURLTypes
22 |
23 |
24 | CFBundleTypeRole
25 | Editor
26 | CFBundleURLSchemes
27 |
28 | flutterexample
29 |
30 |
31 |
32 | CFBundleVersion
33 | $(FLUTTER_BUILD_NUMBER)
34 | LSApplicationQueriesSchemes
35 |
36 | kftc-bankpay
37 | ispmobile
38 | itms-apps
39 | hdcardappcardansimclick
40 | smhyundaiansimclick
41 | shinhan-sr-ansimclick
42 | smshinhanansimclick
43 | kb-acp
44 | mpocket.online.ansimclick
45 | ansimclickscard
46 | ansimclickipcollect
47 | vguardstart
48 | samsungpay
49 | scardcertiapp
50 | lottesmartpay
51 | lotteappcard
52 | cloudpay
53 | nhappcardansimclick
54 | nonghyupcardansimclick
55 | citispay
56 | citicardappkr
57 | citimobileapp
58 | kakaotalk
59 | payco
60 | lpayapp
61 | hanamopmoasign
62 | wooripay
63 | nhallonepayansimclick
64 | hanawalletmembers
65 | liivbank
66 | supertoss
67 | newsmartpib
68 | v3mobileplusweb
69 | kbbank
70 | newliiv
71 | naversearchthirdlogin
72 |
73 | LSRequiresIPhoneOS
74 |
75 | NSAppTransportSecurity
76 |
77 | NSAllowsArbitraryLoads
78 |
79 | NSAllowsArbitraryLoadsInWebContent
80 |
81 |
82 | UILaunchStoryboardName
83 | LaunchScreen
84 | UIMainStoryboardFile
85 | Main
86 | UISupportedInterfaceOrientations
87 |
88 | UIInterfaceOrientationPortrait
89 | UIInterfaceOrientationLandscapeLeft
90 | UIInterfaceOrientationLandscapeRight
91 |
92 | UISupportedInterfaceOrientations~ipad
93 |
94 | UIInterfaceOrientationPortrait
95 | UIInterfaceOrientationPortraitUpsideDown
96 | UIInterfaceOrientationLandscapeLeft
97 | UIInterfaceOrientationLandscapeRight
98 |
99 | UIViewControllerBasedStatusBarAppearance
100 |
101 | CADisableMinimumFrameDurationOnPhone
102 |
103 | UIApplicationSupportsIndirectInputEvents
104 |
105 |
106 |
107 |
--------------------------------------------------------------------------------
/example/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/example/ios/Runner/main.m:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 | #import "AppDelegate.h"
4 |
5 | int main(int argc, char* argv[]) {
6 | @autoreleasepool {
7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/example/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/services.dart';
3 | import 'package:get/get.dart';
4 | import 'package:portone_flutter_example/screens/certification.dart';
5 | import 'package:portone_flutter_example/screens/certification_result.dart';
6 | import 'package:portone_flutter_example/screens/certification_test.dart';
7 | import 'package:portone_flutter_example/screens/home.dart';
8 | import 'package:portone_flutter_example/screens/payment.dart';
9 | import 'package:portone_flutter_example/screens/payment_result.dart';
10 | import 'package:portone_flutter_example/screens/payment_test.dart';
11 |
12 | void main() {
13 | runApp(IamportApp());
14 | }
15 |
16 | class IamportApp extends StatefulWidget {
17 | @override
18 | _IamportAppState createState() => _IamportAppState();
19 | }
20 |
21 | class _IamportAppState extends State {
22 | static const Color primaryColor = Color(0xff344e81);
23 |
24 | @override
25 | Widget build(BuildContext context) {
26 | SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
27 | systemNavigationBarColor: Colors.transparent,
28 | statusBarColor: Colors.transparent,
29 | ));
30 | SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
31 |
32 | return GetMaterialApp(
33 | initialRoute: '/',
34 | theme: ThemeData(
35 | primaryColor: primaryColor,
36 | ),
37 | getPages: [
38 | GetPage(name: '/', page: () => Home()),
39 | GetPage(name: '/payment-test', page: () => PaymentTest()),
40 | GetPage(name: '/payment', page: () => Payment()),
41 | GetPage(name: '/payment-result', page: () => PaymentResult()),
42 | GetPage(name: '/certification-test', page: () => CertificationTest()),
43 | GetPage(name: '/certification', page: () => Certification()),
44 | GetPage(
45 | name: '/certification-result', page: () => CertificationResult()),
46 | ],
47 | );
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/example/lib/model/carrier.dart:
--------------------------------------------------------------------------------
1 | class Carrier {
2 | static List CARRIERS = [
3 | 'SKT',
4 | 'KTF',
5 | 'LGT',
6 | 'MVNO',
7 | ];
8 |
9 | static String getLabel(String pg) {
10 | switch (pg) {
11 | case 'SKT':
12 | return 'SKT';
13 | case 'KTF':
14 | return 'KT';
15 | case 'LGT':
16 | return 'LGU+';
17 | case 'MVNO':
18 | return '알뜰폰';
19 | default:
20 | return '-';
21 | }
22 | }
23 |
24 | static List getLists() {
25 | return CARRIERS;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/example/lib/model/method.dart:
--------------------------------------------------------------------------------
1 | class Method {
2 | static List METHODS = [
3 | 'card',
4 | 'trans',
5 | 'vbank',
6 | 'phone',
7 | ];
8 |
9 | static List METHODS_FOR_INICIS = METHODS +
10 | [
11 | 'samsung',
12 | 'kpay',
13 | 'cultureland',
14 | 'smartculture',
15 | 'happymoney',
16 | ];
17 |
18 | static List METHODS_FOR_UPLUS = METHODS +
19 | [
20 | 'cultureland',
21 | 'smartculture',
22 | 'booknlife',
23 | ];
24 |
25 | static List METHODS_FOR_KCP = METHODS + ['samsung', 'naverpay'];
26 |
27 | static List METHODS_FOR_MOBILIANS = ['card', 'phone'];
28 | static List METHOD_FOR_CARD = ['card'];
29 | static List METHOD_FOR_PHONE = ['phone'];
30 | static List METHOD_FOR_VBANK = ['vbank'];
31 | static List METHOD_FOR_TRANS = ['trans'];
32 | static List METHOD_FOR_TOSSPAY_V2 = ['tosspay'];
33 |
34 | static String getLabel(String method) {
35 | switch (method) {
36 | case 'card':
37 | return '신용카드';
38 | case 'vbank':
39 | return '가상계좌';
40 | case 'trans':
41 | return '실시간 계좌이체';
42 | case 'phone':
43 | return '휴대폰 소액결제';
44 | case 'samsung':
45 | return '삼성페이';
46 | case 'kpay':
47 | return 'KPAY';
48 | case 'cultureland':
49 | return '문화상품권';
50 | case 'smartculture':
51 | return '스마트문상';
52 | case 'happymoney':
53 | return '해피머니';
54 | case 'booknlife':
55 | return '도서상품권';
56 | case 'naverpay':
57 | return '네이버페이';
58 | case 'tosspay':
59 | return '토스페이';
60 | default:
61 | return '-';
62 | }
63 | }
64 |
65 | static String getValueByPg(String pg) {
66 | switch (pg) {
67 | case 'danal':
68 | return 'phone';
69 | case 'settle':
70 | return 'vbank';
71 | case 'chai':
72 | case 'payple':
73 | return 'trans';
74 | case 'tosspay_v2':
75 | return 'tosspay';
76 | default:
77 | return 'card';
78 | }
79 | }
80 |
81 | static List getListsByPg(String pg) {
82 | switch (pg) {
83 | case 'html5_inicis':
84 | return METHODS_FOR_INICIS;
85 | case 'kcp':
86 | return METHODS_FOR_KCP;
87 | case 'kcp_billing':
88 | case 'kakaopay':
89 | case 'kakao':
90 | case 'paypal':
91 | case 'payco':
92 | case 'eximbay':
93 | case 'smilepay':
94 | case 'alipay':
95 | return METHOD_FOR_CARD;
96 | case 'uplus':
97 | return METHODS_FOR_UPLUS;
98 | case 'danal':
99 | return METHOD_FOR_PHONE;
100 | case 'mobilians':
101 | return METHODS_FOR_MOBILIANS;
102 | case 'settle':
103 | return METHOD_FOR_VBANK;
104 | case 'chai':
105 | case 'payple':
106 | return METHOD_FOR_TRANS;
107 | case 'tosspay_v2':
108 | return METHOD_FOR_TOSSPAY_V2;
109 | default:
110 | return METHODS;
111 | }
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/example/lib/model/pg.dart:
--------------------------------------------------------------------------------
1 | class Pg {
2 | static List PGS = [
3 | 'html5_inicis',
4 | 'kcp',
5 | 'kcp_billing',
6 | 'uplus',
7 | 'jtnet',
8 | 'nice',
9 | 'kakaopay',
10 | 'danal',
11 | 'danal_tpay',
12 | 'kicc',
13 | 'paypal',
14 | 'mobilians',
15 | 'payco',
16 | 'eximbay',
17 | 'settle',
18 | 'naverpay',
19 | 'smilepay',
20 | 'chai',
21 | 'payple',
22 | 'alipay',
23 | 'bluewalnut',
24 | 'tosspay',
25 | 'smartro',
26 | 'tosspayments',
27 | 'ksnet',
28 | 'welcome',
29 | 'tosspay_v2'
30 | ];
31 |
32 | static String getLabel(String pg) {
33 | switch (pg) {
34 | case 'html5_inicis':
35 | return '웹 표준 이니시스';
36 | case 'kcp':
37 | return 'NHN KCP';
38 | case 'kcp_billing':
39 | return 'NHN KCP 정기결제';
40 | case 'uplus':
41 | return '(구)토스페이먼츠';
42 | case 'jtnet':
43 | return 'JTNET';
44 | case 'nice':
45 | return '나이스 정보통신';
46 | case 'kakaopay':
47 | return '카카오페이';
48 | case 'danal':
49 | return '다날 휴대폰 소액결제';
50 | case 'danal_tpay':
51 | return '다날 일반결제';
52 | case 'kicc':
53 | return '한국정보통신';
54 | case 'paypal':
55 | return '페이팔';
56 | case 'mobilians':
57 | return '모빌리언스';
58 | case 'payco':
59 | return '페이코';
60 | case 'eximbay':
61 | return '엑심베이';
62 | case 'settle':
63 | return '세틀뱅크 가상계좌';
64 | case 'naverpay':
65 | return '네이버페이';
66 | case 'smilepay':
67 | return '스마일페이';
68 | case 'chai':
69 | return '차이페이';
70 | case 'payple':
71 | return '페이플';
72 | case 'alipay':
73 | return '알리페이';
74 | case 'bluewalnut':
75 | return '블루월넛';
76 | case 'tosspay':
77 | return '토스 간편결제';
78 | case 'smartro':
79 | return '스마트로';
80 | case 'tosspayments':
81 | return '토스페이먼츠';
82 | case 'ksnet':
83 | return 'KSNET';
84 | case 'welcome':
85 | return '웰컴페이먼츠';
86 | case 'tosspay_v2':
87 | return '토스페이(V2)';
88 | default:
89 | return '-';
90 | }
91 | }
92 |
93 | static List getLists() {
94 | return PGS;
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/example/lib/model/quota.dart:
--------------------------------------------------------------------------------
1 | class Quota {
2 | static List QUOTAS = [
3 | '0',
4 | '1',
5 | ];
6 |
7 | static String getLabel(String quota) {
8 | switch (quota) {
9 | case '0':
10 | return 'PG사 기본 제공';
11 | case '1':
12 | return '일시불';
13 | case '2':
14 | return '2개월';
15 | case '3':
16 | return '3개월';
17 | case '4':
18 | return '4개월';
19 | case '5':
20 | return '5개월';
21 | case '6':
22 | return '6개월';
23 | default:
24 | return '-';
25 | }
26 | }
27 |
28 | static List getListsByPg(String pg) {
29 | switch (pg) {
30 | case 'html5_inicis':
31 | case 'kcp':
32 | return QUOTAS + ['2', '3', '4', '5', '6'];
33 | default:
34 | return QUOTAS;
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/example/lib/screens/certification.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:get/get.dart';
3 | import 'package:portone_flutter/iamport_certification.dart';
4 | import 'package:portone_flutter/model/certification_data.dart';
5 |
6 | class Certification extends StatelessWidget {
7 | @override
8 | Widget build(BuildContext context) {
9 | String userCode = Get.arguments['userCode'] as String;
10 | CertificationData data = Get.arguments['data'] as CertificationData;
11 |
12 | return IamportCertification(
13 | appBar: AppBar(
14 | title: Text('포트원 V1 본인인증'),
15 | centerTitle: true,
16 | titleTextStyle: TextStyle(
17 | fontSize: 24,
18 | color: Colors.white,
19 | ),
20 | backgroundColor: Colors.blue,
21 | leading: IconButton(
22 | icon: Icon(Icons.arrow_back_ios),
23 | onPressed: () {
24 | Get.back();
25 | },
26 | ),
27 | ),
28 | initialChild: SafeArea(
29 | child: Center(
30 | child: Column(
31 | mainAxisAlignment: MainAxisAlignment.center,
32 | children: [
33 | Image.asset('assets/images/iamport-logo.png'),
34 | Padding(padding: EdgeInsets.symmetric(vertical: 15)),
35 | Text('잠시만 기다려주세요...', style: TextStyle(fontSize: 20.0)),
36 | ],
37 | ),
38 | ),
39 | ),
40 | userCode: userCode,
41 | data: data,
42 | callback: (Map result) {
43 | Get.offNamed('/certification-result', arguments: result);
44 | },
45 | );
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/example/lib/screens/certification_result.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:get/get.dart';
3 |
4 | class CertificationResult extends StatelessWidget {
5 | static const Color successColor = Color(0xff52c41a);
6 | static const Color failureColor = Color(0xfff5222d);
7 |
8 | @override
9 | Widget build(BuildContext context) {
10 | Map result = Get.arguments as Map;
11 | String message;
12 | IconData icon;
13 | Color color;
14 | bool isErrorMessageRendering;
15 | if (result['success'] == 'true') {
16 | message = '본인인증에 성공하였습니다';
17 | icon = Icons.check_circle;
18 | color = successColor;
19 | isErrorMessageRendering = false;
20 | } else {
21 | message = '본인인증에 실패하였습니다';
22 | icon = Icons.error;
23 | color = failureColor;
24 | isErrorMessageRendering = true;
25 | }
26 |
27 | return Scaffold(
28 | appBar: AppBar(
29 | title: Text('본인인증 결과'),
30 | centerTitle: true,
31 | automaticallyImplyLeading: false,
32 | ),
33 | body: SafeArea(
34 | child: Column(
35 | mainAxisAlignment: MainAxisAlignment.center,
36 | children: [
37 | Icon(
38 | icon,
39 | color: color,
40 | size: 200,
41 | ),
42 | Text(
43 | message,
44 | style: TextStyle(
45 | fontWeight: FontWeight.bold,
46 | fontSize: 20,
47 | ),
48 | ),
49 | Container(
50 | padding: EdgeInsets.fromLTRB(50, 30, 50, 50),
51 | child: Column(
52 | children: [
53 | Container(
54 | padding: EdgeInsets.symmetric(vertical: 5),
55 | child: Row(
56 | mainAxisAlignment: MainAxisAlignment.start,
57 | children: [
58 | Expanded(
59 | flex: 4,
60 | child: Text('포트원 고유 결제번호',
61 | style: TextStyle(color: Colors.grey))),
62 | Expanded(
63 | flex: 5,
64 | child: Text(result['imp_uid'] != null
65 | ? result['imp_uid']!
66 | : ""),
67 | ),
68 | ],
69 | ),
70 | ),
71 | if (isErrorMessageRendering)
72 | Container(
73 | padding: EdgeInsets.symmetric(vertical: 5),
74 | child: Row(
75 | mainAxisAlignment: MainAxisAlignment.start,
76 | children: [
77 | Expanded(
78 | flex: 4,
79 | child: Text('에러 메시지',
80 | style: TextStyle(color: Colors.grey)),
81 | ),
82 | Expanded(
83 | flex: 5,
84 | child: Text(result['error_msg'] != null
85 | ? result['error_msg']!
86 | : ""),
87 | ),
88 | ],
89 | ),
90 | ),
91 | ],
92 | ),
93 | ),
94 | ElevatedButton.icon(
95 | icon: Icon(Icons.arrow_back),
96 | onPressed: () {
97 | Get.offAllNamed('/');
98 | },
99 | label: Text(
100 | '돌아가기',
101 | style: TextStyle(fontSize: 16, color: Colors.white),
102 | ),
103 | style: ElevatedButton.styleFrom(
104 | elevation: 0,
105 | shadowColor: Colors.transparent,
106 | ),
107 | ),
108 | ],
109 | ),
110 | ),
111 | );
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/example/lib/screens/certification_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:get/get.dart';
3 | import 'package:portone_flutter/model/certification_data.dart';
4 | import 'package:portone_flutter_example/model/carrier.dart';
5 | import 'package:portone_flutter/model/url_data.dart';
6 |
7 | class CertificationTest extends StatefulWidget {
8 | @override
9 | _CertificationTestState createState() => _CertificationTestState();
10 | }
11 |
12 | class _CertificationTestState extends State {
13 | final _formKey = GlobalKey();
14 | late String userCode; // 가맹점 식별코드
15 | String pg = 'danal'; // PG사
16 | late String merchantUid; // 주문번호
17 | String company = '포트원'; // 회사명 또는 URL
18 | String carrier = 'SKT'; // 통신사
19 | late String name; // 본인인증 할 이름
20 | late String phone; // 본인인증 할 전화번호
21 | late String minAge; // 최소 허용 만 나이
22 |
23 | @override
24 | Widget build(BuildContext context) {
25 | return Scaffold(
26 | appBar: AppBar(
27 | title: Text('포트원 V1 본인인증 테스트'),
28 | centerTitle: true,
29 | titleTextStyle: TextStyle(
30 | fontSize: 24,
31 | color: Colors.white,
32 | ),
33 | backgroundColor: Colors.blue,
34 | leading: IconButton(
35 | icon: Icon(Icons.arrow_back_ios),
36 | onPressed: () {
37 | Get.back();
38 | },
39 | ),
40 | ),
41 | body: SafeArea(
42 | minimum: EdgeInsets.symmetric(horizontal: 15),
43 | child: Form(
44 | key: _formKey,
45 | child: ListView(
46 | children: [
47 | TextFormField(
48 | decoration: InputDecoration(
49 | labelText: '가맹점 식별코드',
50 | ),
51 | validator: (value) =>
52 | value!.isEmpty ? '가맹점 식별코드는 필수입력입니다' : null,
53 | initialValue: '',
54 | onSaved: (String? value) {
55 | userCode = value!;
56 | },
57 | ),
58 | DropdownButtonFormField(
59 | decoration: InputDecoration(
60 | labelText: 'PG사',
61 | ),
62 | value: pg,
63 | onChanged: (String? value) {
64 | setState(() {
65 | pg = value!;
66 | });
67 | },
68 | items: ['danal', 'inicis_unified']
69 | .map>((String value) {
70 | return DropdownMenuItem(
71 | value: value,
72 | child: Text(value == 'danal'
73 | ? '다날 휴대폰 본인인증'
74 | : (value == 'inicis_unified' ? '이니시스 통합인증' : '')),
75 | );
76 | }).toList(),
77 | ),
78 | TextFormField(
79 | decoration: InputDecoration(
80 | labelText: '주문번호',
81 | ),
82 | validator: (value) => value!.isEmpty ? '주문번호는 필수입력입니다' : null,
83 | initialValue: 'mid_${DateTime.now().millisecondsSinceEpoch}',
84 | onSaved: (String? value) {
85 | merchantUid = value!;
86 | },
87 | ),
88 | Visibility(
89 | child: TextFormField(
90 | initialValue: company,
91 | decoration: InputDecoration(
92 | labelText: '회사명',
93 | ),
94 | onSaved: (String? value) {
95 | company = value!;
96 | },
97 | ),
98 | visible: pg == 'danal',
99 | ),
100 | Visibility(
101 | child: DropdownButtonFormField(
102 | decoration: InputDecoration(
103 | labelText: '통신사',
104 | ),
105 | value: carrier,
106 | onChanged: (String? value) {
107 | setState(() {
108 | carrier = value!;
109 | });
110 | },
111 | items: Carrier.getLists()
112 | .map>((String value) {
113 | return DropdownMenuItem(
114 | value: value,
115 | child: Text(Carrier.getLabel(value)),
116 | );
117 | }).toList(),
118 | ),
119 | visible: pg == 'danal',
120 | ),
121 | Visibility(
122 | child: TextFormField(
123 | decoration: InputDecoration(
124 | labelText: '이름',
125 | hintText: '본인인증 할 이름',
126 | ),
127 | onSaved: (String? value) {
128 | name = value!;
129 | },
130 | ),
131 | visible: pg == 'danal',
132 | ),
133 | Visibility(
134 | child: TextFormField(
135 | decoration: InputDecoration(
136 | labelText: '전화번호',
137 | hintText: '본인인증 할 전화번호',
138 | ),
139 | keyboardType: TextInputType.number,
140 | onSaved: (String? value) {
141 | phone = value!;
142 | },
143 | ),
144 | visible: pg == 'danal',
145 | ),
146 | Visibility(
147 | child: TextFormField(
148 | decoration: InputDecoration(
149 | labelText: '최소연령',
150 | hintText: '허용 최소 만 나이',
151 | ),
152 | validator: (value) {
153 | if (value!.length > 0) {
154 | RegExp regex = RegExp(r'^[0-9]+$');
155 | if (!regex.hasMatch(value)) return '최소 연령이 올바르지 않습니다.';
156 | }
157 | return null;
158 | },
159 | keyboardType: TextInputType.number,
160 | onSaved: (String? value) {
161 | minAge = value!;
162 | },
163 | ),
164 | visible: pg == 'danal',
165 | ),
166 | Container(
167 | padding: EdgeInsets.symmetric(vertical: 10),
168 | child: ElevatedButton(
169 | onPressed: () {
170 | if (_formKey.currentState!.validate()) {
171 | _formKey.currentState!.save();
172 |
173 | CertificationData data = CertificationData(
174 | pg: pg,
175 | merchantUid: merchantUid,
176 | );
177 |
178 | if (pg == 'inicis_unified') {
179 | data.mRedirectUrl = UrlData.redirectUrl;
180 | } else {
181 | data.carrier = carrier;
182 | data.company = company;
183 | data.name = name;
184 | data.phone = phone;
185 | if (minAge.length > 0) {
186 | data.minAge = int.parse(minAge);
187 | }
188 | }
189 |
190 | Get.toNamed(
191 | '/certification',
192 | arguments: {
193 | 'userCode': userCode,
194 | 'data': data,
195 | },
196 | );
197 | }
198 | },
199 | child: Text(
200 | '본인인증 하기',
201 | style: TextStyle(
202 | fontSize: 25,
203 | color: Colors.white,
204 | fontWeight: FontWeight.bold,
205 | ),
206 | ),
207 | style: ElevatedButton.styleFrom(
208 | padding: EdgeInsets.symmetric(vertical: 10),
209 | shape: RoundedRectangleBorder(
210 | borderRadius: BorderRadius.circular(25),
211 | ),
212 | elevation: 0,
213 | shadowColor: Colors.transparent,
214 | backgroundColor: Colors.blue,
215 | ),
216 | ),
217 | ),
218 | ],
219 | ),
220 | ),
221 | ),
222 | );
223 | }
224 | }
225 |
--------------------------------------------------------------------------------
/example/lib/screens/home.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:get/get.dart';
3 |
4 | class Home extends StatefulWidget {
5 | @override
6 | _HomeState createState() => _HomeState();
7 | }
8 |
9 | class _HomeState extends State {
10 | @override
11 | Widget build(BuildContext context) {
12 | return Material(
13 | child: Container(
14 | decoration: BoxDecoration(color: Color(0xff344e81)),
15 | child: Center(
16 | child: Column(
17 | mainAxisAlignment: MainAxisAlignment.center,
18 | children: [
19 | Row(
20 | mainAxisAlignment: MainAxisAlignment.center,
21 | children: [
22 | Padding(
23 | padding: const EdgeInsets.only(
24 | left: 0,
25 | top: 20.0,
26 | right: 0,
27 | bottom: 20.0,
28 | ),
29 | child: Text(
30 | '포트원 V1 테스트',
31 | style: TextStyle(
32 | fontSize: 24.0,
33 | color: Colors.white,
34 | height: 2.0,
35 | ),
36 | ),
37 | ),
38 | ],
39 | ),
40 | Row(
41 | mainAxisAlignment: MainAxisAlignment.center,
42 | children: [
43 | Text(
44 | '포트원 V1 플러터 모듈 테스트 화면입니다.',
45 | style: TextStyle(
46 | fontSize: 14.0,
47 | color: Colors.white,
48 | height: 1.2,
49 | ),
50 | ),
51 | ],
52 | ),
53 | Row(
54 | mainAxisAlignment: MainAxisAlignment.center,
55 | children: [
56 | Text(
57 | '아래 버튼을 눌러 결제 또는 본인인증 테스트를 진행해주세요.',
58 | style: TextStyle(
59 | fontSize: 14.0,
60 | color: Colors.white,
61 | height: 1.2,
62 | ),
63 | ),
64 | ],
65 | ),
66 | Padding(
67 | padding: const EdgeInsets.only(
68 | left: 0,
69 | top: 50.0,
70 | right: 0,
71 | bottom: 0,
72 | ),
73 | child: Row(
74 | mainAxisAlignment: MainAxisAlignment.center,
75 | children: [
76 | Padding(
77 | padding: EdgeInsets.only(
78 | left: 20,
79 | ),
80 | ),
81 | Expanded(
82 | child: ElevatedButton.icon(
83 | style: ElevatedButton.styleFrom(
84 | padding: EdgeInsets.symmetric(vertical: 10),
85 | shape: RoundedRectangleBorder(
86 | borderRadius: BorderRadius.circular(20),
87 | ),
88 | elevation: 0,
89 | shadowColor: Colors.transparent,
90 | ),
91 | icon: Icon(
92 | Icons.payment,
93 | color: Colors.black,
94 | size: 25,
95 | ),
96 | label: Text(
97 | '결제 테스트',
98 | style: TextStyle(
99 | color: Colors.black,
100 | fontSize: 15,
101 | fontWeight: FontWeight.bold,
102 | ),
103 | ),
104 | onPressed: () {
105 | Get.toNamed('/payment-test');
106 | },
107 | ),
108 | ),
109 | Padding(
110 | padding: EdgeInsets.symmetric(horizontal: 10),
111 | ),
112 | Expanded(
113 | child: ElevatedButton.icon(
114 | style: ElevatedButton.styleFrom(
115 | padding: EdgeInsets.symmetric(vertical: 10),
116 | shape: RoundedRectangleBorder(
117 | borderRadius: BorderRadius.circular(20),
118 | ),
119 | elevation: 0,
120 | shadowColor: Colors.transparent,
121 | ),
122 | icon: Icon(
123 | Icons.people,
124 | color: Colors.black,
125 | size: 25,
126 | ),
127 | label: Text(
128 | '본인인증 테스트',
129 | style: TextStyle(
130 | color: Colors.black,
131 | fontSize: 15,
132 | fontWeight: FontWeight.bold,
133 | ),
134 | ),
135 | onPressed: () {
136 | Get.toNamed('/certification-test');
137 | },
138 | ),
139 | ),
140 | Padding(padding: EdgeInsets.only(right: 20)),
141 | ],
142 | ),
143 | ),
144 | ],
145 | ),
146 | ),
147 | ),
148 | );
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/example/lib/screens/payment.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:get/get.dart';
3 | import 'package:portone_flutter/iamport_payment.dart';
4 | import 'package:portone_flutter/model/payment_data.dart';
5 |
6 | class Payment extends StatelessWidget {
7 | @override
8 | Widget build(BuildContext context) {
9 | String userCode = Get.arguments['userCode'] as String;
10 | PaymentData data = Get.arguments['data'] as PaymentData;
11 |
12 | return IamportPayment(
13 | appBar: AppBar(
14 | title: Text('포트원 V1 결제'),
15 | centerTitle: true,
16 | titleTextStyle: TextStyle(
17 | fontSize: 24,
18 | color: Colors.white,
19 | ),
20 | backgroundColor: Colors.blue,
21 | leading: IconButton(
22 | icon: Icon(Icons.arrow_back_ios),
23 | onPressed: () {
24 | Get.back();
25 | },
26 | ),
27 | ),
28 | initialChild: SafeArea(
29 | child: Center(
30 | child: Column(
31 | mainAxisAlignment: MainAxisAlignment.center,
32 | children: [
33 | Image.asset('assets/images/iamport-logo.png'),
34 | Padding(padding: EdgeInsets.symmetric(vertical: 15)),
35 | Text('잠시만 기다려주세요...', style: TextStyle(fontSize: 20.0)),
36 | ],
37 | ),
38 | ),
39 | ),
40 | userCode: userCode,
41 | data: data,
42 | callback: (Map result) {
43 | Get.offNamed('/payment-result', arguments: result);
44 | },
45 | );
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/example/lib/screens/payment_result.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:get/get.dart';
3 |
4 | typedef PaymentResultPayload = Map;
5 |
6 | extension SuccessResult on PaymentResultPayload {
7 | bool get isSuccessed {
8 | bool? getBoolean(String? value) {
9 | switch (value) {
10 | case "true":
11 | return true;
12 | case "false":
13 | return false;
14 | }
15 | return null;
16 | }
17 |
18 | return getBoolean(this["imp_success"]) ??
19 | getBoolean(this['success']) ??
20 | this['error_code'] == null && this['code'] == null;
21 | }
22 |
23 | String get transactionId {
24 | return this['imp_uid'] ?? this['txId'] ?? '-';
25 | }
26 |
27 | String get paymentId {
28 | return this['merchant_uid'] ?? this['paymentId'] ?? '-';
29 | }
30 |
31 | String get errorCode {
32 | return this['error_code'] ?? this['code'] ?? '-';
33 | }
34 |
35 | String get errorMessage {
36 | return this['error_msg'] ?? this['message'] ?? '-';
37 | }
38 | }
39 |
40 | class PaymentResult extends StatelessWidget {
41 | static const Color successColor = Color(0xff52c41a);
42 | static const Color failureColor = Color(0xfff5222d);
43 |
44 | @override
45 | Widget build(BuildContext context) {
46 | PaymentResultPayload payload = Get.arguments as PaymentResultPayload;
47 | bool isSuccessed = payload.isSuccessed;
48 | String message;
49 | IconData icon;
50 | Color color;
51 | if (isSuccessed) {
52 | message = '결제에 성공하였습니다';
53 | icon = Icons.check_circle;
54 | color = successColor;
55 | } else {
56 | message = '결제에 실패하였습니다';
57 | icon = Icons.error;
58 | color = failureColor;
59 | }
60 |
61 | return Scaffold(
62 | appBar: AppBar(
63 | title: Text('포트원 V1 결제 결과'),
64 | centerTitle: true,
65 | automaticallyImplyLeading: false,
66 | ),
67 | body: SafeArea(
68 | child: Column(
69 | mainAxisAlignment: MainAxisAlignment.center,
70 | children: [
71 | Icon(
72 | icon,
73 | color: color,
74 | size: 200,
75 | ),
76 | Text(
77 | message,
78 | style: TextStyle(
79 | fontWeight: FontWeight.bold,
80 | fontSize: 20,
81 | ),
82 | ),
83 | Container(
84 | padding: EdgeInsets.fromLTRB(50, 30, 50, 50),
85 | child: Column(
86 | children: [
87 | Container(
88 | padding: EdgeInsets.symmetric(vertical: 5),
89 | child: Row(
90 | mainAxisAlignment: MainAxisAlignment.start,
91 | children: [
92 | Expanded(
93 | flex: 4,
94 | child: Text('포트원 고유 결제번호',
95 | style: TextStyle(color: Colors.grey))),
96 | Expanded(
97 | flex: 5,
98 | child: Text(payload.transactionId),
99 | ),
100 | ],
101 | ),
102 | ),
103 | isSuccessed
104 | ? Container(
105 | padding: EdgeInsets.symmetric(vertical: 5),
106 | child: Row(
107 | mainAxisAlignment: MainAxisAlignment.start,
108 | children: [
109 | Expanded(
110 | flex: 4,
111 | child: Text('주문 번호',
112 | style: TextStyle(color: Colors.grey))),
113 | Expanded(
114 | flex: 5,
115 | child: Text(payload.paymentId),
116 | ),
117 | ],
118 | ),
119 | )
120 | : Container(
121 | padding: EdgeInsets.symmetric(vertical: 5),
122 | child: Column(
123 | children: [
124 | Row(
125 | mainAxisAlignment: MainAxisAlignment.start,
126 | children: [
127 | Expanded(
128 | flex: 4,
129 | child: Text('에러 코드',
130 | style: TextStyle(color: Colors.grey)),
131 | ),
132 | Expanded(
133 | flex: 5,
134 | child: Text(payload.errorCode),
135 | )
136 | ]),
137 | Row(
138 | mainAxisAlignment: MainAxisAlignment.start,
139 | children: [
140 | Expanded(
141 | flex: 4,
142 | child: Text('에러 메시지',
143 | style: TextStyle(color: Colors.grey)),
144 | ),
145 | Expanded(
146 | flex: 5,
147 | child: Text(payload.errorMessage),
148 | ),
149 | ])
150 | ],
151 | ),
152 | ),
153 | ],
154 | ),
155 | ),
156 | ElevatedButton.icon(
157 | icon: Icon(Icons.arrow_back),
158 | onPressed: () {
159 | Get.offAllNamed('/');
160 | },
161 | label: Text(
162 | '돌아가기',
163 | style: TextStyle(fontSize: 16, color: Colors.white),
164 | ),
165 | style: ElevatedButton.styleFrom(
166 | elevation: 0,
167 | shadowColor: Colors.transparent,
168 | ),
169 | ),
170 | ],
171 | ),
172 | ),
173 | );
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/example/manuals/CALLBACK.md:
--------------------------------------------------------------------------------
1 | # 콜백 함수 설정하기
2 | 포트원 V1 플러터 모듈 콜백 함수 설정을 위한 안내입니다.
3 |
4 | 콜백 함수는 필수입력 필드로, 결제/본인인증 완료 후 라우트를 이동하도록 로직을 작성해야합니다. 아래와 같이 [push](https://api.flutter.dev/flutter/widgets/Navigator/push.html) 함수가 아닌 [pushReplacementNamed](https://api.flutter.dev/flutter/widgets/Navigator/pushReplacementNamed.html) 함수를 사용해야 합니다.
5 | `push` 함수를 사용할 경우, 결제/본인인증 완료 후 라우터가 변경되더라도 유저가 뒤로가기를 하면 포트원 V1 모듈이 다시 렌더링됩니다. 하지만 `pushReplacementNamed` 함수를 사용하면, 결제/본인인증 직전 화면으로 넘어가게 됩니다.
6 |
7 | ### 잘못된 사용 예제
8 | ```dart
9 | callback: (Map result) {
10 | Navigator.push(
11 | context,
12 | '/result',
13 | arguments: result,
14 | );
15 | },
16 | ```
17 |
18 | ### 올바른 사용 예제
19 | ```dart
20 | callback: (Map result) {
21 | Navigator.pushReplacementNamed(
22 | context,
23 | '/result',
24 | arguments: result,
25 | );
26 | },
27 | ```
28 |
29 | ### 결과에 따라 로직 작성하기
30 | 콜백 함수의 첫번째 인자(result)는 결제/본인인증 결과를 담고 있는 오브젝트로 아래와 같이 구성되어 있습니다. 자세한 내용은 포트원 개발자센터 [인증 결제 연동하기 - 3. 결제 결과 처리하기](https://developers.portone.io/opi/ko/integration/start/v1/auth?v=v1#3-%EA%B2%B0%EC%A0%9C-%EA%B2%B0%EA%B3%BC-%EC%B2%98%EB%A6%AC%ED%95%98%EA%B8%B0-)를 참고해주세요.
31 |
32 | | key | Description |
33 | | ------------- | ------------------ |
34 | | success | 성공 여부 |
35 | | imp_uid | 포트원 고유 결제번호 |
36 | | merchant_uid | 주문번호 |
37 | | error_msg | 실패한 경우, 에러메시지 |
38 |
39 | response에 따라 결제/본인인증 성공/실패 여부를 판단해 아래와 같이 각기 다른 로직을 구성할 수 있습니다. 아래 코드는 예시일 뿐 실제 결제 성공/실패여부는 결제 유효성 검사 후 포트원 V1 REST API로 결제내역을 조회해 판단해야 합니다. 자세한 내용은 포트원 V1 공식 문서 [인증 결제 연동하기 - 4. 결제 완료 처리하기](https://developers.portone.io/opi/ko/integration/start/v1/auth?v=v1#4-%EA%B2%B0%EC%A0%9C-%EC%99%84%EB%A3%8C-%EC%B2%98%EB%A6%AC%ED%95%98%EA%B8%B0-)를 참고해주세요.
40 |
41 | ```dart
42 | import 'package:flutter/material.dart';
43 |
44 | class Result extends StatelessWidget {
45 | bool getIsSuccessed(Map result) {
46 | if (result['imp_success'] == 'true') {
47 | return true;
48 | }
49 | if (result['success'] == 'true') {
50 | return true;
51 | }
52 | return false;
53 | }
54 |
55 | @override
56 | Widget build(BuildContext context) {
57 | Map result = ModalRoute.of(context).settings.arguments;
58 | bool isSuccessed = getIsSuccessed(result);
59 |
60 | return Scaffold(
61 | appBar: new AppBar(
62 | title: Text('포트원 V1 결과'),
63 | ),
64 | body: Column(
65 | mainAxisAlignment: MainAxisAlignment.center,
66 | children: [
67 | Text(
68 | message: isSuccessed ? '성공하였습니다' : '실패하였습니다',
69 | style: TextStyle(
70 | fontWeight: FontWeight.bold,
71 | fontSize: 20.0,
72 | ),
73 | ),
74 | Container(
75 | padding: EdgeInsets.fromLTRB(50.0, 30.0, 50.0, 50.0),
76 | child: Column(
77 | children: [
78 | Container(
79 | padding: EdgeInsets.fromLTRB(0, 5.0, 0, 5.0),
80 | child: Row(
81 | mainAxisAlignment: MainAxisAlignment.start,
82 | children: [
83 | Expanded(
84 | flex: 4,
85 | child: Text('포트원 고유 결제번호', style: TextStyle(color: Colors.grey))
86 | ),
87 | Expanded(
88 | flex: 5,
89 | child: Text(result['imp_uid'] ?? '-'),
90 | ),
91 | ],
92 | ),
93 | ),
94 | isSuccessed ? Container(
95 | padding: EdgeInsets.fromLTRB(0, 5.0, 0, 5.0),
96 | child: Row(
97 | mainAxisAlignment: MainAxisAlignment.start,
98 | children: [
99 | Expanded(
100 | flex: 4,
101 | child: Text('주문 번호', style: TextStyle(color: Colors.grey))
102 | ),
103 | Expanded(
104 | flex: 5,
105 | child: Text(result['merchant_uid'] ?? '-'),
106 | ),
107 | ],
108 | ),
109 | ) : Container(
110 | padding: EdgeInsets.fromLTRB(0, 5.0, 0, 5.0),
111 | child: Row(
112 | mainAxisAlignment: MainAxisAlignment.start,
113 | children: [
114 | Expanded(
115 | flex: 4,
116 | child: Text('에러 메시지', style: TextStyle(color: Colors.grey)),
117 | ),
118 | Expanded(
119 | flex: 5,
120 | child: Text(result['error_msg'] ?? '-'),
121 | ),
122 | ],
123 | ),
124 | ),
125 | ],
126 | ),
127 | ),
128 | ],
129 | ),
130 | );
131 | }
132 | }
133 | ```
--------------------------------------------------------------------------------
/example/manuals/TRANS.md:
--------------------------------------------------------------------------------
1 | # 실시간 계좌이체 설정하기
2 |
3 | ## Android - 웹 표준 이니시스 또는 나이스 정보통신
4 | Android에서 `웹 표준 이니시스(이하 이니시스)` 또는 `나이스` `실시간 계좌이체`를 연동하는 경우 별도 설정이 요구됩니다. 이니시스는 결제완료 후 콜백이 실행되지 않고, 나이스는 결제인증 후 결제완료 처리가 되지 않기 때문입니다. 이는 이니시스와 나이스 결제모듈 자체 문제입니다. 포트원 V1 플러터 모듈은 이에 대응하기 위한 안내를 제공하고 있습니다.
5 |
6 | ### 실시간 계좌이체 결제처리 원리
7 | 먼저 뱅크페이 앱에서 귀하의 앱으로 복귀할때를 트리거해야합니다. 포트원 V1 플러터 모듈은 트리거 된 순간 이니시스의 경우 콜백을 실행시키고, 나이스의 경우 나이스로 결제정보가 담긴 POST 요청을 보내야 합니다. Java는 들어오는(`incoming`) 앱 링크를 트리거 하기 위해 `deep linking` 기능을 제공합니다.
8 |
9 | ### Intent Filter 추가하고 launchMode 설정하기
10 | deep linking 기능을 활성화하기 위해 `Intent Filter`를 추가하고 `MainActivity`의 `launchMode`를 아래와 같이 `singleTask`로 설정해야 합니다. 자세한 내용은 [Create Deep Links to App Content](https://developer.android.com/training/app-links/deep-linking)를 참고하세요.
11 |
12 | ```xml
13 | ...
14 |
15 |
16 |
20 | ...
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | ...
30 | ```
--------------------------------------------------------------------------------
/example/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: portone_flutter_example
2 | description: Demonstrates how to use the portone_flutter plugin.
3 | publish_to: "none"
4 |
5 | version: 1.0.0+1
6 |
7 | environment:
8 | sdk: ">=2.13.0 <4.0.0"
9 |
10 | dependencies:
11 | flutter:
12 | sdk: flutter
13 | # The following adds the Cupertino Icons font to your application.
14 | # Use with the CupertinoIcons class for iOS style icons.
15 | cupertino_icons: ^1.0.3
16 | get: ^4.6.0
17 |
18 | dev_dependencies:
19 | flutter_test:
20 | sdk: flutter
21 |
22 | portone_flutter:
23 | path: ../
24 |
25 | # For information on the generic Dart part of this file, see the
26 | # following page: https://www.dartlang.org/tools/pub/pubspec
27 |
28 | # The following section is specific to Flutter.
29 | flutter:
30 | # The following line ensures that the Material Icons font is
31 | # included with your application, so that you can use the icons in
32 | # the material Icons class.
33 | uses-material-design: true
34 |
35 | # To add assets to your application, add an assets section, like this:
36 | assets:
37 | - assets/images/iamport-logo.png
38 |
39 | # An image asset can refer to one or more resolution-specific "variants", see
40 | # https://flutter.dev/assets-and-images/#resolution-aware.
41 |
42 | # For details regarding adding assets from package dependencies, see
43 | # https://flutter.dev/assets-and-images/#from-packages
44 |
45 | # To add custom fonts to your application, add a fonts section here,
46 | # in this "flutter" section. Each entry in this list should have a
47 | # "family" key with the font family name, and a "fonts" key with a
48 | # list giving the asset and other descriptors for the font. For
49 | # example:
50 | # fonts:
51 | # - family: Schyler
52 | # fonts:
53 | # - asset: fonts/Schyler-Regular.ttf
54 | # - asset: fonts/Schyler-Italic.ttf
55 | # style: italic
56 | # - family: Trajan Pro
57 | # fonts:
58 | # - asset: fonts/TrajanPro.ttf
59 | # - asset: fonts/TrajanPro_Bold.ttf
60 | # weight: 700
61 | #
62 | # For details regarding fonts from package dependencies,
63 | # see https://flutter.dev/custom-fonts/#from-packages
64 | module:
65 | androidX: true
66 |
--------------------------------------------------------------------------------
/example/test/widget_test.dart:
--------------------------------------------------------------------------------
1 | // This is a basic Flutter widget test.
2 | //
3 | // To perform an interaction with a widget in your test, use the WidgetTester
4 | // utility that Flutter provides. For example, you can send tap and scroll
5 | // gestures. You can also use WidgetTester to find child widgets in the widget
6 | // tree, read text, and verify that the values of widget properties are correct.
7 |
8 | import 'package:flutter/material.dart';
9 | import 'package:flutter_test/flutter_test.dart';
10 |
11 | import 'package:portone_flutter_example/main.dart';
12 |
13 | void main() {
14 | testWidgets('Verify Platform version', (WidgetTester tester) async {
15 | // Build our app and trigger a frame.
16 | await tester.pumpWidget(IamportApp());
17 |
18 | // Verify that platform version is retrieved.
19 | expect(
20 | find.byWidgetPredicate(
21 | (Widget widget) =>
22 | widget is Text && widget.data!.startsWith('Running on:'),
23 | ),
24 | findsOneWidget,
25 | );
26 | });
27 | }
28 |
--------------------------------------------------------------------------------
/ios/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | .vagrant/
3 | .sconsign.dblite
4 | .svn/
5 |
6 | .DS_Store
7 | *.swp
8 | profile
9 |
10 | DerivedData/
11 | build/
12 | GeneratedPluginRegistrant.h
13 | GeneratedPluginRegistrant.m
14 |
15 | .generated/
16 |
17 | *.pbxuser
18 | *.mode1v3
19 | *.mode2v3
20 | *.perspectivev3
21 |
22 | !default.pbxuser
23 | !default.mode1v3
24 | !default.mode2v3
25 | !default.perspectivev3
26 |
27 | xcuserdata
28 |
29 | *.moved-aside
30 |
31 | *.pyc
32 | *sync/
33 | Icon?
34 | .tags*
35 |
36 | /Flutter/Generated.xcconfig
37 |
--------------------------------------------------------------------------------
/ios/Assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/portone-io/portone_flutter/4a8c02372ab1a53fb52a4485f8c730bb138c3a2c/ios/Assets/.gitkeep
--------------------------------------------------------------------------------
/ios/Classes/IamportFlutterPlugin.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface IamportFlutterPlugin : NSObject
4 | @end
5 |
--------------------------------------------------------------------------------
/ios/Classes/IamportFlutterPlugin.m:
--------------------------------------------------------------------------------
1 | #import "IamportFlutterPlugin.h"
2 |
3 | #if __has_include()
4 | #import
5 | #else
6 | // Support project import fallback if the generated compatibility header
7 | // is not copied when this plugin is created as a library.
8 | // https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816
9 | #import "flutter_plugin_example-Swift.h"
10 | #import "portone_flutter-Swift.h"
11 | #endif
12 |
13 | @implementation IamportFlutterPlugin
14 | + (void)registerWithRegistrar:(NSObject*)registrar {
15 | [SwiftIamportFlutterPlugin registerWithRegistrar:registrar];
16 | }
17 | @end
--------------------------------------------------------------------------------
/ios/Classes/SwiftIamportFlutterPlugin.swift:
--------------------------------------------------------------------------------
1 | import Flutter
2 | import UIKit
3 |
4 | public class SwiftIamportFlutterPlugin: NSObject, FlutterPlugin {
5 | public static func register(with registrar: FlutterPluginRegistrar) {
6 | let channel = FlutterMethodChannel(name: "portone_flutter", binaryMessenger: registrar.messenger())
7 | let instance = SwiftIamportFlutterPlugin()
8 | registrar.addMethodCallDelegate(instance, channel: channel)
9 | }
10 |
11 | public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
12 | result("iOS " + UIDevice.current.systemVersion)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/ios/portone_flutter.podspec:
--------------------------------------------------------------------------------
1 | #
2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
3 | #
4 | Pod::Spec.new do |s|
5 | s.name = 'portone_flutter'
6 | s.version = '0.0.1'
7 | s.summary = 'A new flutter plugin project.'
8 | s.description = <<-DESC
9 | A new flutter plugin project.
10 | DESC
11 | s.homepage = 'http://example.com'
12 | s.license = { :file => '../LICENSE' }
13 | s.author = { 'Your Company' => 'email@example.com' }
14 | s.source = { :path => '.' }
15 | s.source_files = 'Classes/**/*'
16 | s.dependency 'Flutter'
17 | s.platform = :ios, '8.0'
18 |
19 | # Flutter.framework does not contain a i386 slice.
20 | s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
21 | s.swift_version = '5.0'
22 | end
23 |
24 |
--------------------------------------------------------------------------------
/lib/Iamport_certification.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 | import 'dart:core';
3 |
4 | import 'package:flutter/cupertino.dart';
5 | import 'package:flutter/foundation.dart';
6 | import 'package:flutter/gestures.dart';
7 | import 'package:flutter/material.dart';
8 | import 'package:portone_flutter/model/certification_data.dart';
9 | import 'package:portone_flutter/model/iamport_validation.dart';
10 | import 'package:portone_flutter/model/url_data.dart';
11 | import 'package:portone_flutter/widget/iamport_error.dart';
12 | import 'package:portone_flutter/widget/iamport_webview.dart';
13 | import 'package:iamport_webview_flutter/iamport_webview_flutter.dart';
14 |
15 | class IamportCertification extends StatelessWidget {
16 | final PreferredSizeWidget? appBar;
17 | final Widget? initialChild;
18 | final String userCode;
19 | final String? tierCode;
20 | final CertificationData data;
21 | final callback;
22 | final Set>? gestureRecognizers;
23 | final String? customUserAgent;
24 |
25 | IamportCertification({
26 | Key? key,
27 | this.appBar,
28 | this.initialChild,
29 | required this.userCode,
30 | this.tierCode,
31 | required this.data,
32 | required this.callback,
33 | this.gestureRecognizers,
34 | this.customUserAgent,
35 | }) : super(key: key);
36 |
37 | @override
38 | Widget build(BuildContext context) {
39 | var redirectUrl = UrlData.redirectUrl;
40 | if (this.data.mRedirectUrl != null && this.data.mRedirectUrl!.isNotEmpty) {
41 | redirectUrl = this.data.mRedirectUrl!;
42 | }
43 |
44 | IamportValidation validation = IamportValidation.fromCertificationData(
45 | userCode,
46 | data,
47 | callback,
48 | );
49 |
50 | var init =
51 | this.tierCode == null
52 | ? 'IMP.init("${this.userCode}");'
53 | : 'IMP.agency("${this.userCode}", "${this.tierCode}");';
54 |
55 | if (validation.getIsValid()) {
56 | return IamportWebView(
57 | type: ActionType.auth,
58 | appBar: this.appBar,
59 | initialChild: this.initialChild,
60 | gestureRecognizers: this.gestureRecognizers,
61 | customUserAgent: this.customUserAgent,
62 | executeJS: (WebViewController controller) {
63 | controller.evaluateJavascript('''
64 | $init
65 | IMP.certification(${jsonEncode(this.data.toJson())}, function(response) {
66 | const query = [];
67 | Object.keys(response).forEach(function(key) {
68 | query.push(key + "=" + response[key]);
69 | });
70 | location.href = "$redirectUrl" + "?" + query.join("&");
71 | });
72 | ''');
73 | },
74 | useQueryData: (Map data) {
75 | this.callback(data);
76 | },
77 | isPaymentOver: (String url) {
78 | return url.startsWith(redirectUrl);
79 | },
80 | // 인증에는 customPGAction 수행할 필요 없음
81 | customPGAction: (WebViewController controller) {},
82 | );
83 | } else {
84 | return IamportError(ActionType.auth, validation.getErrorMessage());
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/lib/iamport_payment.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:convert';
3 | import 'dart:io';
4 |
5 | import 'package:flutter/cupertino.dart';
6 | import 'package:flutter/foundation.dart';
7 | import 'package:flutter/gestures.dart';
8 | import 'package:flutter/material.dart';
9 | import 'package:portone_flutter/model/iamport_validation.dart';
10 | import 'package:portone_flutter/model/payment_data.dart';
11 | import 'package:portone_flutter/model/url_data.dart';
12 | import 'package:portone_flutter/widget/iamport_error.dart';
13 | import 'package:portone_flutter/widget/iamport_webview.dart';
14 | import 'package:iamport_webview_flutter/iamport_webview_flutter.dart';
15 | import 'package:app_links/app_links.dart';
16 |
17 | class IamportPayment extends StatelessWidget {
18 | final PreferredSizeWidget? appBar;
19 | final Widget? initialChild;
20 | final String userCode;
21 | final String? tierCode;
22 | final PaymentData data;
23 | final callback;
24 | final Set>? gestureRecognizers;
25 | final _appLinks = AppLinks();
26 |
27 | IamportPayment({
28 | Key? key,
29 | this.appBar,
30 | this.initialChild,
31 | required this.userCode,
32 | this.tierCode,
33 | required this.data,
34 | required this.callback,
35 | this.gestureRecognizers,
36 | }) : super(key: key);
37 |
38 | @override
39 | Widget build(BuildContext context) {
40 | IamportValidation validation = IamportValidation(
41 | this.userCode,
42 | this.data,
43 | this.callback,
44 | );
45 |
46 | if (validation.getIsValid()) {
47 | var redirectUrl = UrlData.redirectUrl;
48 | if (this.data.mRedirectUrl != null &&
49 | this.data.mRedirectUrl!.isNotEmpty) {
50 | redirectUrl = this.data.mRedirectUrl!;
51 | }
52 |
53 | var init =
54 | this.tierCode == null
55 | ? 'IMP.init("${this.userCode}");'
56 | : 'IMP.agency("${this.userCode}", "${this.tierCode}");';
57 |
58 | return IamportWebView(
59 | type: ActionType.payment,
60 | appBar: this.appBar,
61 | initialChild: this.initialChild,
62 | gestureRecognizers: this.gestureRecognizers,
63 | executeJS: (WebViewController controller) {
64 | controller.evaluateJavascript('''
65 | $init
66 | IMP.request_pay(${jsonEncode(this.data.toJson())}, function(response) {
67 | const query = [];
68 | Object.keys(response).forEach(function(key) {
69 | query.push(key + "=" + response[key]);
70 | });
71 | location.href = "$redirectUrl" + "?" + query.join("&");
72 | });
73 | ''');
74 | },
75 | customPGAction: (WebViewController controller) {
76 | if (this.data.pg == 'smilepay') {
77 | // webview_flutter에서 iOS는 쿠키가 기본적으로 허용되어있는 것으로 추정
78 | if (Platform.isAndroid) {
79 | controller.setAcceptThirdPartyCookies(true);
80 | }
81 | }
82 | /* [v0.9.6] niceMobileV2: true 대비 코드 작성 */
83 | if (this.data.pg == 'nice' && this.data.payMethod == 'trans') {
84 | try {
85 | StreamSubscription sub = _appLinks.uriLinkStream.listen((
86 | Uri? link,
87 | ) async {
88 | if (link != null) {
89 | String decodedUrl = Uri.decodeComponent(link.toString());
90 | Uri parsedUrl = Uri.parse(decodedUrl);
91 | String scheme = parsedUrl.scheme;
92 | if (scheme == data.appScheme.toLowerCase()) {
93 | String queryToString = parsedUrl.query;
94 | String? niceTransRedirectionUrl;
95 | parsedUrl.queryParameters.forEach((key, value) {
96 | if (key == 'callbackparam1') {
97 | niceTransRedirectionUrl = value;
98 | }
99 | });
100 | await controller.evaluateJavascript('''
101 | location.href = "$niceTransRedirectionUrl?$queryToString";
102 | ''');
103 | }
104 | }
105 | });
106 | return sub;
107 | } on FormatException {}
108 | }
109 | return null;
110 | },
111 | useQueryData: (Map data) {
112 | this.callback(data);
113 | },
114 | isPaymentOver: (String url) {
115 | if (url.startsWith(redirectUrl)) {
116 | return true;
117 | }
118 |
119 | if (this.data.payMethod == 'trans') {
120 | /* [IOS] imp_uid와 merchant_uid값만 전달되기 때문에 결제 성공 또는 실패 구분할 수 없음 */
121 | String decodedUrl = Uri.decodeComponent(url);
122 | Uri parsedUrl = Uri.parse(decodedUrl);
123 | String scheme = parsedUrl.scheme;
124 | if (this.data.pg == 'html5_inicis') {
125 | Map query = parsedUrl.queryParameters;
126 | if (query['m_redirect_url'] != null &&
127 | scheme == this.data.appScheme.toLowerCase()) {
128 | if (query['m_redirect_url']!.contains(redirectUrl)) {
129 | return true;
130 | }
131 | }
132 | }
133 | }
134 |
135 | return false;
136 | },
137 | );
138 | } else {
139 | return IamportError(ActionType.payment, validation.getErrorMessage());
140 | }
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/lib/model/certification_data.dart:
--------------------------------------------------------------------------------
1 | import 'package:json_annotation/json_annotation.dart';
2 |
3 | part 'certification_data.g.dart';
4 |
5 | @JsonSerializable()
6 | class CertificationData {
7 | String? pg;
8 |
9 | @JsonKey(name: 'merchant_uid')
10 | String? merchantUid;
11 |
12 | String? company;
13 | String? carrier;
14 | String? name;
15 | String? phone;
16 |
17 | @JsonKey(name: 'min_age')
18 | int? minAge;
19 |
20 | bool? popup;
21 |
22 | @JsonKey(name: 'm_redirect_url')
23 | String? mRedirectUrl;
24 |
25 | CertificationData({
26 | this.pg,
27 | this.merchantUid,
28 | this.company,
29 | this.carrier,
30 | this.name,
31 | this.phone,
32 | this.minAge,
33 | this.popup,
34 | this.mRedirectUrl,
35 | });
36 |
37 | factory CertificationData.fromJson(Map json) =>
38 | _$CertificationDataFromJson(json);
39 |
40 | Map toJson() => _$CertificationDataToJson(this);
41 | }
42 |
--------------------------------------------------------------------------------
/lib/model/certification_data.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'certification_data.dart';
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | CertificationData _$CertificationDataFromJson(Map json) =>
10 | CertificationData(
11 | pg: json['pg'] as String?,
12 | merchantUid: json['merchant_uid'] as String?,
13 | company: json['company'] as String?,
14 | carrier: json['carrier'] as String?,
15 | name: json['name'] as String?,
16 | phone: json['phone'] as String?,
17 | minAge: json['min_age'] as int?,
18 | popup: json['popup'] as bool?,
19 | mRedirectUrl: json['m_redirect_url'] as String?,
20 | );
21 |
22 | Map _$CertificationDataToJson(CertificationData instance) {
23 | final val = {};
24 |
25 | void writeNotNull(String key, dynamic value) {
26 | if (value != null) {
27 | val[key] = value;
28 | }
29 | }
30 |
31 | writeNotNull('pg', instance.pg);
32 | writeNotNull('merchant_uid', instance.merchantUid);
33 | writeNotNull('company', instance.company);
34 | writeNotNull('carrier', instance.carrier);
35 | writeNotNull('name', instance.name);
36 | writeNotNull('phone', instance.phone);
37 | writeNotNull('min_age', instance.minAge);
38 | writeNotNull('popup', instance.popup);
39 | writeNotNull('m_redirect_url', instance.mRedirectUrl);
40 | return val;
41 | }
42 |
--------------------------------------------------------------------------------
/lib/model/iamport_url.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:io';
3 |
4 | import 'package:portone_flutter/model/url_data.dart';
5 | import 'package:url_launcher/url_launcher_string.dart';
6 |
7 | class IamportUrl {
8 | late String url;
9 | String? appScheme;
10 | String? appUrl;
11 | String? package; // Android only
12 |
13 | IamportUrl(String incomeUrl) {
14 | this.url = incomeUrl;
15 |
16 | List splittedUrl =
17 | this.url.replaceFirst(RegExp(r'://'), ' ').split(' ');
18 | this.appScheme = splittedUrl[0];
19 |
20 | if (Platform.isAndroid) {
21 | /*
22 | Android scheme은 크게 3가지 형태
23 | 1. intent://
24 | 2. [app]://
25 | 3. intent:[app]://
26 | 이 세가지를 정상적으로 launch가 가능한 2번 형태로 변환한다
27 | */
28 | if (this.isAppLink()) {
29 | if (this.appScheme!.contains('intent')) {
30 | List intentUrl = splittedUrl[1].split('#Intent;');
31 | String host = intentUrl[0];
32 | // 농협카드 일반결제 예외처리
33 | if (host.contains(':')) {
34 | host = host.replaceAll(RegExp(r':'), '%3A');
35 | }
36 | List arguments = intentUrl[1].split(';');
37 |
38 | // scheme이 intent로 시작하면 뒷쪽의 정보를 통해 appscheme과 package 정보 추출
39 | if (this.appScheme! != 'intent') {
40 | // 현대카드 예외처리
41 | this.appScheme = this.appScheme!.split(':')[1];
42 | this.appUrl = this.appScheme! + '://' + host;
43 | }
44 | arguments.forEach((s) {
45 | if (s.startsWith('scheme')) {
46 | String scheme = s.split('=')[1];
47 | this.appUrl = scheme + '://' + host;
48 | this.appScheme = scheme;
49 | } else if (s.startsWith('package')) {
50 | String package = s.split('=')[1];
51 | this.package = package;
52 | }
53 | });
54 | } else {
55 | this.appUrl = this.url;
56 | }
57 | } else {
58 | this.appUrl = this.url;
59 | }
60 | } else {
61 | this.appUrl = this.url;
62 | }
63 | }
64 |
65 | bool isAppLink() {
66 | String? scheme;
67 | try {
68 | scheme = Uri.parse(this.url).scheme;
69 | } catch (e) {
70 | scheme = this.appScheme;
71 | }
72 |
73 | if (Platform.isAndroid && this.appScheme == 'https') {
74 | if (this
75 | .url
76 | .startsWith('https://play.google.com/store/apps/details?id=')) {
77 | return true;
78 | }
79 | }
80 |
81 | return !['http', 'https', 'about', 'data', ''].contains(scheme);
82 | }
83 |
84 | Future getAppUrl() async {
85 | return this.appUrl;
86 | }
87 |
88 | Future getMarketUrl() async {
89 | if (Platform.isIOS) {
90 | switch (this.appScheme) {
91 | case 'kftc-bankpay': // 뱅크페이
92 | return UrlData.IOS_MARKET_PREFIX + 'id398456030';
93 | case 'ispmobile': // ISP/페이북
94 | return UrlData.IOS_MARKET_PREFIX + 'id369125087';
95 | case 'hdcardappcardansimclick': // 현대카드 앱카드
96 | return UrlData.IOS_MARKET_PREFIX + 'id702653088';
97 | case 'shinhan-sr-ansimclick': // 신한 앱카드
98 | return UrlData.IOS_MARKET_PREFIX + 'id572462317';
99 | case 'kb-acp': // KB국민 앱카드
100 | return UrlData.IOS_MARKET_PREFIX + 'id695436326';
101 | case 'mpocket.online.ansimclick': // 삼성앱카드
102 | return UrlData.IOS_MARKET_PREFIX + 'id535125356';
103 | case 'lottesmartpay': // 롯데 모바일결제
104 | return UrlData.IOS_MARKET_PREFIX + 'id668497947';
105 | case 'lotteappcard': // 롯데 앱카드
106 | return UrlData.IOS_MARKET_PREFIX + 'id688047200';
107 | case 'cloudpay': // 하나1Q페이(앱카드)
108 | return UrlData.IOS_MARKET_PREFIX + 'id847268987';
109 | case 'citimobileapp': // 시티은행 앱카드
110 | return UrlData.IOS_MARKET_PREFIX + 'id1179759666';
111 | case 'payco': // 페이코
112 | return UrlData.IOS_MARKET_PREFIX + 'id924292102';
113 | case 'kakaotalk': // 카카오톡
114 | return UrlData.IOS_MARKET_PREFIX + 'id362057947';
115 | case 'lpayapp': // 롯데 L.pay
116 | return UrlData.IOS_MARKET_PREFIX + 'id1036098908';
117 | case 'wooripay': // 우리페이
118 | return UrlData.IOS_MARKET_PREFIX + 'id1201113419';
119 | case 'com.wooricard.wcard': // 우리WON카드
120 | return UrlData.IOS_MARKET_PREFIX + 'id1499598869';
121 | case 'nhallonepayansimclick': // NH농협카드 올원페이(앱카드)
122 | return UrlData.IOS_MARKET_PREFIX + 'id1177889176';
123 | case 'hanawalletmembers': // 하나카드(하나멤버스 월렛)
124 | return UrlData.IOS_MARKET_PREFIX + 'id1038288833';
125 | case 'shinsegaeeasypayment': // 신세계 SSGPAY
126 | return UrlData.IOS_MARKET_PREFIX + 'id666237916';
127 | case 'naversearchthirdlogin': // 네이버페이 앱 로그인
128 | return UrlData.IOS_MARKET_PREFIX + 'id393499958';
129 | case 'lguthepay-xpay': // 페이나우
130 | return UrlData.IOS_MARKET_PREFIX + 'id760098906';
131 | case 'lmslpay': // 롯데 L.POINT
132 | return UrlData.IOS_MARKET_PREFIX + 'id473250588';
133 | case 'liivbank': // Liiv 국민
134 | return UrlData.IOS_MARKET_PREFIX + 'id1126232922';
135 | case 'supertoss': // 토스
136 | return UrlData.IOS_MARKET_PREFIX + 'id839333328';
137 | case 'newsmartpib': // 우리WON뱅킹
138 | return UrlData.IOS_MARKET_PREFIX + 'id1470181651';
139 | case 'v3mobileplusweb': // V3 Mobile Plus
140 | return UrlData.IOS_MARKET_PREFIX + 'id1481938658';
141 | case 'kbbank': // KB스타뱅킹
142 | return UrlData.IOS_MARKET_PREFIX + 'id373742138';
143 | case 'newliiv': // 리브 Next
144 | return UrlData.IOS_MARKET_PREFIX + 'id1573528126';
145 | default:
146 | return this.url;
147 | }
148 | } else if (Platform.isAndroid) {
149 | if (this.package != null) {
150 | // 앱이 설치되어 있지 않아 실행 불가능할 경우 추출된 package 정보를 이용해 플레이스토어 열기
151 | return UrlData.ANDROID_MARKET_PREFIX + this.package!;
152 | }
153 | switch (this.appScheme) {
154 | case UrlData.ISP:
155 | return UrlData.ANDROID_MARKET_PREFIX + UrlData.PACKAGE_ISP;
156 | case UrlData.BANKPAY:
157 | return UrlData.ANDROID_MARKET_PREFIX + UrlData.PACKAGE_BANKPAY;
158 | case UrlData.KB_BANKPAY:
159 | return UrlData.ANDROID_MARKET_PREFIX + UrlData.PACKAGE_KB_BANKPAY;
160 | case UrlData.NH_BANKPAY:
161 | return UrlData.ANDROID_MARKET_PREFIX + UrlData.PACKAGE_NH_BANKPAY;
162 | case UrlData.MG_BANKPAY:
163 | return UrlData.ANDROID_MARKET_PREFIX + UrlData.PACKAGE_MG_BANKPAY;
164 | case UrlData.KN_BANKPAY:
165 | return UrlData.ANDROID_MARKET_PREFIX + UrlData.PACKAGE_KN_BANKPAY;
166 | case UrlData.KAKAOPAY:
167 | return UrlData.ANDROID_MARKET_PREFIX + UrlData.PACKAGE_KAKAOPAY;
168 | case UrlData.SMILEPAY:
169 | return UrlData.ANDROID_MARKET_PREFIX + UrlData.PACKAGE_SMILEPAY;
170 | case UrlData.CHAIPAY:
171 | return UrlData.ANDROID_MARKET_PREFIX + UrlData.PACKAGE_CHAIPAY;
172 | case UrlData.PAYCO:
173 | return UrlData.ANDROID_MARKET_PREFIX + UrlData.PACKAGE_PAYCO;
174 | case UrlData.HYUNDAICARD:
175 | return UrlData.ANDROID_MARKET_PREFIX + UrlData.PACKAGE_HYUNDAICARD;
176 | case UrlData.TOSS:
177 | return UrlData.ANDROID_MARKET_PREFIX + UrlData.PACKAGE_TOSS;
178 | case UrlData.SHINHANCARD:
179 | return UrlData.ANDROID_MARKET_PREFIX + UrlData.PACKAGE_SHINHANCARD;
180 | case UrlData.HANACARD:
181 | return UrlData.ANDROID_MARKET_PREFIX + UrlData.PACKAGE_HANACARD;
182 | case UrlData.SAMSUNGCARD:
183 | return UrlData.ANDROID_MARKET_PREFIX + UrlData.PACKAGE_SAMSUNGCARD;
184 | case UrlData.KBCARD:
185 | return UrlData.ANDROID_MARKET_PREFIX + UrlData.PACKAGE_KBCARD;
186 | case UrlData.NHCARD:
187 | return UrlData.ANDROID_MARKET_PREFIX + UrlData.PACKAGE_NHCARD;
188 | case UrlData.CITICARD:
189 | return UrlData.ANDROID_MARKET_PREFIX + UrlData.PACKAGE_CITICARD;
190 | case UrlData.LOTTECARD:
191 | return UrlData.ANDROID_MARKET_PREFIX + UrlData.PACKAGE_LOTTECARD;
192 | case UrlData.LPAY:
193 | return UrlData.ANDROID_MARKET_PREFIX + UrlData.PACKAGE_LPAY;
194 | case UrlData.SSGPAY:
195 | return UrlData.ANDROID_MARKET_PREFIX + UrlData.PACKAGE_SSGPAY;
196 | case UrlData.KPAY:
197 | return UrlData.ANDROID_MARKET_PREFIX + UrlData.PACKAGE_KPAY;
198 | case UrlData.PAYNOW:
199 | return UrlData.ANDROID_MARKET_PREFIX + UrlData.PACKAGE_PAYNOW;
200 | case UrlData.WOORIWONCARD:
201 | return UrlData.ANDROID_MARKET_PREFIX + UrlData.PACKAGE_WOORIWONCARD;
202 | case UrlData.LPOINT:
203 | return UrlData.ANDROID_MARKET_PREFIX + UrlData.PACKAGE_LPOINT;
204 | case UrlData.WOORIWONBANK:
205 | return UrlData.ANDROID_MARKET_PREFIX + UrlData.PACKAGE_WOORIWONBANK;
206 | case UrlData.KTFAUTH:
207 | return UrlData.ANDROID_MARKET_PREFIX + UrlData.PACKAGE_KTFAUTH;
208 | case UrlData.LGTAUTH:
209 | return UrlData.ANDROID_MARKET_PREFIX + UrlData.PACKAGE_LGTAUTH;
210 | case UrlData.SKTAUTH:
211 | return UrlData.ANDROID_MARKET_PREFIX + UrlData.PACKAGE_SKTAUTH;
212 | case UrlData.V3_MOBILE_PLUS:
213 | return UrlData.ANDROID_MARKET_PREFIX + UrlData.PACKAGE_V3_MOBILE_PLUS;
214 | case UrlData.KBBANK:
215 | return UrlData.ANDROID_MARKET_PREFIX + UrlData.PACKAGE_KBBANK;
216 | case UrlData.LIIV_NEXT:
217 | return UrlData.ANDROID_MARKET_PREFIX + UrlData.PACKAGE_LIIV_NEXT;
218 | case UrlData.NAVER:
219 | return UrlData.ANDROID_MARKET_PREFIX + UrlData.PACKAGE_NAVER;
220 | default:
221 | return this.url;
222 | }
223 | }
224 | return null;
225 | }
226 |
227 | Future launchApp() async {
228 | bool opened = false;
229 | String appUrl = (await this.getAppUrl())!;
230 |
231 | try {
232 | opened = await launchUrlString(appUrl);
233 | } catch (e) {}
234 |
235 | if (!opened) {
236 | opened = await launchUrlString((await this.getMarketUrl())!);
237 | }
238 |
239 | return opened;
240 | }
241 | }
242 |
--------------------------------------------------------------------------------
/lib/model/iamport_validation.dart:
--------------------------------------------------------------------------------
1 | import 'package:portone_flutter/model/certification_data.dart';
2 | import 'package:portone_flutter/model/payment_data.dart';
3 |
4 | class IamportValidation {
5 | bool isValid = true;
6 | String? errorMessage;
7 |
8 | IamportValidation(String userCode, PaymentData data, Function callback) {
9 | if (data.payMethod == 'vbank') {
10 | if (data.vbankDue == null) {
11 | isValid = false;
12 | errorMessage = '가상계좌 결제시 입금기한(vbankDue)은 필수입력입니다.';
13 | return;
14 | }
15 |
16 | if (data.pg == 'danal_tpay' && data.bizNum == null) {
17 | isValid = false;
18 | errorMessage = '다날 - 가상계좌 결제시 사업자 등록번호(bizNum)은 필수입력입니다.';
19 | return;
20 | }
21 | }
22 |
23 | if (data.payMethod == 'phone' && data.digital == null) {
24 | isValid = false;
25 | errorMessage = '휴대폰 소액결제시 실물 컨텐츠 여부(digital)는 필수입력입니다.';
26 | return;
27 | }
28 |
29 | if (data.pg == 'kcp_billing' && data.customerUid == null) {
30 | isValid = false;
31 | errorMessage = '정기결제시 구매자 카드정보(customerUid)는 필수입력입니다.';
32 | return;
33 | }
34 |
35 | if (data.pg == 'syrup') {
36 | isValid = false;
37 | errorMessage = '해당 모듈은 해당 PG사를 지원하지 않습니다.';
38 | return;
39 | }
40 |
41 | if (data.pg == 'paypal' && data.popup == true) {
42 | isValid = false;
43 | errorMessage = '해당 모듈에서 페이팔 - 팝업 방식은 지원하지 않습니다.';
44 | return;
45 | }
46 |
47 | if ((data.pg == 'naverpay' || data.pg == 'naverco') &&
48 | data.naverPopupMode == true) {
49 | isValid = false;
50 | errorMessage = '해당 모듈에서 네이버페이 - 팝업 방식은 지원하지 않습니다.';
51 | return;
52 | }
53 |
54 | if (data.popup == true) {
55 | isValid = false;
56 | errorMessage = '해당 모듈은 팝업 방식을 지원하지 않습니다.';
57 | return;
58 | }
59 | }
60 |
61 | IamportValidation.fromCertificationData(
62 | String userCode, CertificationData data, Function callback) {
63 | if (data.popup == true) {
64 | isValid = false;
65 | errorMessage = '해당 모듈은 팝업 방식을 지원하지 않습니다.';
66 | return;
67 | }
68 | }
69 |
70 | bool getIsValid() {
71 | return isValid;
72 | }
73 |
74 | String? getErrorMessage() {
75 | return errorMessage;
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/lib/model/payment_data.dart:
--------------------------------------------------------------------------------
1 | import 'package:portone_flutter/model/pg/bypass.dart';
2 | import 'package:portone_flutter/model/pg/naver/naver_interface.dart';
3 | import 'package:portone_flutter/model/pg/naver/naver_products.dart';
4 | import 'package:portone_flutter/model/pg/kcp/kcp_products.dart';
5 | import 'package:portone_flutter/model/url_data.dart';
6 | import 'package:json_annotation/json_annotation.dart';
7 |
8 | part 'payment_data.g.dart';
9 |
10 | @JsonSerializable()
11 | class PaymentData {
12 | String? pg; // PG사
13 |
14 | @JsonKey(name: 'pay_method')
15 | String payMethod; // 결제수단
16 |
17 | bool? escrow; // 에스크로 여부
18 |
19 | @JsonKey(name: 'merchant_uid')
20 | String merchantUid; // 주문번호
21 |
22 | String? name; // 주문명
23 | num amount; // 결제금액
24 |
25 | @JsonKey(name: 'custom_data')
26 | Map? customData; // 임의 지정 데이터
27 |
28 | @JsonKey(name: 'tax_free')
29 | int? taxFree; // 면세 공급 가액
30 |
31 | int? vat; // 부가세
32 | String? currency; // 화폐단위
33 | String? language; // 언어설정
34 |
35 | @JsonKey(name: 'buyer_name')
36 | String? buyerName; // 구매자 이름
37 |
38 | @JsonKey(name: 'buyer_tel')
39 | String buyerTel; // 구매자 연락처
40 |
41 | @JsonKey(name: 'buyer_email')
42 | String? buyerEmail; // 구매자 이메일
43 |
44 | @JsonKey(name: 'buyer_addr')
45 | String? buyerAddr; // 구매자 주소
46 |
47 | @JsonKey(name: 'buyer_postcode')
48 | String? buyerPostcode; // 구매자 우편번호
49 |
50 | @JsonKey(name: 'notice_url')
51 | String? noticeUrl;
52 |
53 | @JsonKey(
54 | name: 'display',
55 | toJson: _cardQuotaToJson,
56 | fromJson: _cardQuotaFromJson,
57 | )
58 | List? cardQuota; // 할부개월수
59 |
60 | bool? digital; // 실물컨텐츠 여부
61 |
62 | @JsonKey(name: 'vbank_due')
63 | String? vbankDue; // 가상계좌 입금기한
64 |
65 | @JsonKey(name: 'confirm_url')
66 | String? confirmUrl;
67 |
68 | @JsonKey(name: 'm_redirect_url')
69 | String? mRedirectUrl;
70 |
71 | @JsonKey(name: 'app_scheme')
72 | String appScheme; // 앱 스킴
73 |
74 | @JsonKey(name: 'biz_num')
75 | String? bizNum; // 사업자번호
76 |
77 | @JsonKey(name: 'customer_id')
78 | String? customerId; // 회원 고유 id
79 |
80 | @JsonKey(name: 'customer_uid')
81 | String? customerUid; // 정기결제 카드정보
82 |
83 | bool? popup; // 페이팔 팝업 여부
84 | String? naverUseCfm; // 네이버페이 이용완료일
85 | bool? naverPopupMode; // 네이버페이 팝업 여부
86 | List? naverProducts; // 네이버 상품정보
87 | bool? naverCultureBenefit;
88 | String? naverProductCode;
89 | String? naverActionType;
90 | String? naverPurchaserName;
91 | String? naverPurchaserBirthday;
92 | String? naverChainId;
93 | String? naverMerchantUserKey;
94 | NaverInterface? naverInterface;
95 | Map? period; // [이니시스. 다날. 나이스] 서비스 제공기간 표기
96 | String? company; // [다날 - 휴대폰 소액결제 전용] 주문명: (company) name 대비
97 | bool? niceMobileV2 = true;
98 | List? kcpProducts; // kcp 상품정보
99 | Bypass? bypass;
100 |
101 | PaymentData({
102 | this.pg,
103 | required this.payMethod,
104 | this.escrow,
105 | required this.merchantUid,
106 | this.name,
107 | required this.amount,
108 | this.customData,
109 | this.taxFree,
110 | this.vat,
111 | this.currency,
112 | this.language,
113 | this.buyerName,
114 | required this.buyerTel,
115 | this.buyerEmail,
116 | this.buyerAddr,
117 | this.buyerPostcode,
118 | this.noticeUrl,
119 | this.cardQuota,
120 | this.digital,
121 | this.vbankDue,
122 | this.confirmUrl,
123 | this.mRedirectUrl = UrlData.redirectUrl,
124 | required this.appScheme,
125 | this.bizNum,
126 | this.customerId,
127 | this.customerUid,
128 | this.popup,
129 | this.naverUseCfm,
130 | this.naverPopupMode,
131 | this.naverProducts,
132 | this.naverCultureBenefit,
133 | this.naverProductCode,
134 | this.naverActionType,
135 | this.naverPurchaserName,
136 | this.naverPurchaserBirthday,
137 | this.naverChainId,
138 | this.naverMerchantUserKey,
139 | this.naverInterface,
140 | this.period,
141 | this.company,
142 | this.niceMobileV2,
143 | this.kcpProducts,
144 | this.bypass,
145 | });
146 |
147 | factory PaymentData.fromJson(Map json) =>
148 | _$PaymentDataFromJson(json);
149 |
150 | Map toJson() => _$PaymentDataToJson(this);
151 |
152 | static List? _cardQuotaFromJson(Map json) =>
153 | (json['card_quota'] as List?)?.map((e) => e as int).toList();
154 |
155 | static Map? _cardQuotaToJson(List? list) {
156 | if (list != null) {
157 | final val = {};
158 |
159 | val['card_quota'] = list;
160 |
161 | return val;
162 | } else {
163 | return null;
164 | }
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/lib/model/payment_data.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'payment_data.dart';
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | PaymentData _$PaymentDataFromJson(Map json) => PaymentData(
10 | pg: json['pg'] as String?,
11 | payMethod: json['pay_method'] as String,
12 | escrow: json['escrow'] as bool?,
13 | merchantUid: json['merchant_uid'] as String,
14 | name: json['name'] as String?,
15 | amount: json['amount'] as num,
16 | customData: (json['custom_data'] as Map?)?.map(
17 | (k, e) => MapEntry(k, e as String),
18 | ),
19 | taxFree: json['tax_free'] as int?,
20 | vat: json['vat'] as int?,
21 | currency: json['currency'] as String?,
22 | language: json['language'] as String?,
23 | buyerName: json['buyer_name'] as String?,
24 | buyerTel: json['buyer_tel'] as String,
25 | buyerEmail: json['buyer_email'] as String?,
26 | buyerAddr: json['buyer_addr'] as String?,
27 | buyerPostcode: json['buyer_postcode'] as String?,
28 | noticeUrl: json['notice_url'] as String?,
29 | cardQuota: PaymentData._cardQuotaFromJson(
30 | json['display'] as Map),
31 | digital: json['digital'] as bool?,
32 | vbankDue: json['vbank_due'] as String?,
33 | confirmUrl: json['confirm_url'] as String?,
34 | mRedirectUrl: json['m_redirect_url'] as String? ?? UrlData.redirectUrl,
35 | appScheme: json['app_scheme'] as String,
36 | bizNum: json['biz_num'] as String?,
37 | customerId: json['customer_id'] as String?,
38 | customerUid: json['customer_uid'] as String?,
39 | popup: json['popup'] as bool?,
40 | naverUseCfm: json['naverUseCfm'] as String?,
41 | naverPopupMode: json['naverPopupMode'] as bool?,
42 | naverProducts: (json['naverProducts'] as List?)
43 | ?.map((e) => NaverProducts.fromJson(e as Map))
44 | .toList(),
45 | naverCultureBenefit: json['naverCultureBenefit'] as bool?,
46 | naverProductCode: json['naverProductCode'] as String?,
47 | naverActionType: json['naverActionType'] as String?,
48 | naverPurchaserName: json['naverPurchaserName'] as String?,
49 | naverPurchaserBirthday: json['naverPurchaserBirthday'] as String?,
50 | naverChainId: json['naverChainId'] as String?,
51 | naverMerchantUserKey: json['naverMerchantUserKey'] as String?,
52 | naverInterface: json['naverInterface'] == null
53 | ? null
54 | : NaverInterface.fromJson(
55 | json['naverInterface'] as Map),
56 | period: (json['period'] as Map?)?.map(
57 | (k, e) => MapEntry(k, e as String),
58 | ),
59 | company: json['company'] as String?,
60 | niceMobileV2: json['niceMobileV2'] as bool?,
61 | kcpProducts: (json['kcpProducts'] as List?)
62 | ?.map((e) => KcpProducts.fromJson(e as Map))
63 | .toList(),
64 | bypass: json['bypass'] == null
65 | ? null
66 | : Bypass.fromJson(json['bypass'] as Map),
67 | );
68 |
69 | Map _$PaymentDataToJson(PaymentData instance) {
70 | final val = {};
71 |
72 | void writeNotNull(String key, dynamic value) {
73 | if (value != null) {
74 | val[key] = value;
75 | }
76 | }
77 |
78 | writeNotNull('pg', instance.pg);
79 | val['pay_method'] = instance.payMethod;
80 | writeNotNull('escrow', instance.escrow);
81 | val['merchant_uid'] = instance.merchantUid;
82 | writeNotNull('name', instance.name);
83 | val['amount'] = instance.amount;
84 | writeNotNull('custom_data', instance.customData);
85 | writeNotNull('tax_free', instance.taxFree);
86 | writeNotNull('vat', instance.vat);
87 | writeNotNull('currency', instance.currency);
88 | writeNotNull('language', instance.language);
89 | writeNotNull('buyer_name', instance.buyerName);
90 | val['buyer_tel'] = instance.buyerTel;
91 | writeNotNull('buyer_email', instance.buyerEmail);
92 | writeNotNull('buyer_addr', instance.buyerAddr);
93 | writeNotNull('buyer_postcode', instance.buyerPostcode);
94 | writeNotNull('notice_url', instance.noticeUrl);
95 | writeNotNull('display', PaymentData._cardQuotaToJson(instance.cardQuota));
96 | writeNotNull('digital', instance.digital);
97 | writeNotNull('vbank_due', instance.vbankDue);
98 | writeNotNull('confirm_url', instance.confirmUrl);
99 | writeNotNull('m_redirect_url', instance.mRedirectUrl);
100 | val['app_scheme'] = instance.appScheme;
101 | writeNotNull('biz_num', instance.bizNum);
102 | writeNotNull('customer_id', instance.customerId);
103 | writeNotNull('customer_uid', instance.customerUid);
104 | writeNotNull('popup', instance.popup);
105 | writeNotNull('naverUseCfm', instance.naverUseCfm);
106 | writeNotNull('naverPopupMode', instance.naverPopupMode);
107 | writeNotNull('naverProducts', instance.naverProducts);
108 | writeNotNull('naverCultureBenefit', instance.naverCultureBenefit);
109 | writeNotNull('naverProductCode', instance.naverProductCode);
110 | writeNotNull('naverActionType', instance.naverActionType);
111 | writeNotNull('naverPurchaserName', instance.naverPurchaserName);
112 | writeNotNull('naverPurchaserBirthday', instance.naverPurchaserBirthday);
113 | writeNotNull('naverChainId', instance.naverChainId);
114 | writeNotNull('naverMerchantUserKey', instance.naverMerchantUserKey);
115 | writeNotNull('naverInterface', instance.naverInterface);
116 | writeNotNull('period', instance.period);
117 | writeNotNull('company', instance.company);
118 | writeNotNull('niceMobileV2', instance.niceMobileV2);
119 | writeNotNull('kcpProducts', instance.kcpProducts);
120 | writeNotNull('bypass', instance.bypass);
121 | return val;
122 | }
123 |
--------------------------------------------------------------------------------
/lib/model/pg/bypass.dart:
--------------------------------------------------------------------------------
1 | import 'package:portone_flutter/model/pg/danal/danal.dart';
2 | import 'package:portone_flutter/model/pg/daou/daou.dart';
3 | import 'package:portone_flutter/model/pg/tosspayments/tosspayments.dart';
4 | import 'package:portone_flutter/model/pg/settle/settle.dart';
5 | import 'package:json_annotation/json_annotation.dart';
6 |
7 | part 'bypass.g.dart';
8 |
9 | @JsonSerializable()
10 | class Bypass {
11 | bool? isCulturalExpense;
12 | String? cashReceiptType;
13 |
14 | // https://guide.portone.io/aad7f7a4-366b-46fc-8e59-f1d79c986b3e
15 | @JsonKey(name: "acceptmethod")
16 | String? acceptMethod;
17 | @JsonKey(name: "P_RESERVED")
18 | String? pReserved;
19 |
20 | Daou? daou;
21 | Tosspayments? tosspayments;
22 | Settle? settle;
23 | Danal? danal;
24 |
25 | Bypass({
26 | this.isCulturalExpense,
27 | this.cashReceiptType,
28 | this.acceptMethod,
29 | this.pReserved,
30 | this.daou,
31 | this.tosspayments,
32 | this.settle,
33 | this.danal,
34 | });
35 |
36 | factory Bypass.fromJson(Map json) => _$BypassFromJson(json);
37 |
38 | Map toJson() => _$BypassToJson(this);
39 | }
40 |
--------------------------------------------------------------------------------
/lib/model/pg/bypass.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'bypass.dart';
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | Bypass _$BypassFromJson(Map json) => Bypass(
10 | isCulturalExpense: json['isCulturalExpense'] as bool?,
11 | cashReceiptType: json['cashReceiptType'] as String?,
12 | acceptMethod: json['acceptmethod'] as String?,
13 | pReserved: json['P_RESERVED'] as String?,
14 | daou: json['daou'] == null
15 | ? null
16 | : Daou.fromJson(json['daou'] as Map),
17 | tosspayments: json['tosspayments'] == null
18 | ? null
19 | : Tosspayments.fromJson(json['tosspayments'] as Map),
20 | settle: json['settle'] == null
21 | ? null
22 | : Settle.fromJson(json['settle'] as Map),
23 | danal: json['danal'] == null
24 | ? null
25 | : Danal.fromJson(json['danal'] as Map),
26 | );
27 |
28 | Map _$BypassToJson(Bypass instance) {
29 | final val = {};
30 |
31 | void writeNotNull(String key, dynamic value) {
32 | if (value != null) {
33 | val[key] = value;
34 | }
35 | }
36 |
37 | writeNotNull('isCulturalExpense', instance.isCulturalExpense);
38 | writeNotNull('cashReceiptType', instance.cashReceiptType);
39 | writeNotNull('acceptmethod', instance.acceptMethod);
40 | writeNotNull('P_RESERVED', instance.pReserved);
41 | writeNotNull('daou', instance.daou);
42 | writeNotNull('tosspayments', instance.tosspayments);
43 | writeNotNull('settle', instance.settle);
44 | writeNotNull('danal', instance.danal);
45 | return val;
46 | }
47 |
--------------------------------------------------------------------------------
/lib/model/pg/danal/danal.dart:
--------------------------------------------------------------------------------
1 | import 'package:json_annotation/json_annotation.dart';
2 |
3 | part 'danal.g.dart';
4 |
5 | @JsonSerializable()
6 | class Danal {
7 | // https://guide.portone.io/aad7f7a4-366b-46fc-8e59-f1d79c986b3e
8 | @JsonKey(name: "ISCASHRECEIPTUI")
9 | String? isCashReceiptUi;
10 |
11 | Danal({
12 | this.isCashReceiptUi,
13 | });
14 |
15 | factory Danal.fromJson(Map json) => _$DanalFromJson(json);
16 |
17 | Map toJson() => _$DanalToJson(this);
18 | }
19 |
--------------------------------------------------------------------------------
/lib/model/pg/danal/danal.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'danal.dart';
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | Danal _$DanalFromJson(Map json) => Danal(
10 | isCashReceiptUi: json['ISCASHRECEIPTUI'] as String?,
11 | );
12 |
13 | Map _$DanalToJson(Danal instance) {
14 | final val = {};
15 |
16 | void writeNotNull(String key, dynamic value) {
17 | if (value != null) {
18 | val[key] = value;
19 | }
20 | }
21 |
22 | writeNotNull('ISCASHRECEIPTUI', instance.isCashReceiptUi);
23 | return val;
24 | }
25 |
--------------------------------------------------------------------------------
/lib/model/pg/daou/daou.dart:
--------------------------------------------------------------------------------
1 | import 'package:json_annotation/json_annotation.dart';
2 |
3 | part 'daou.g.dart';
4 |
5 | @JsonSerializable()
6 | class Daou {
7 | @JsonKey(name: "PRODUCTCODE")
8 | String? productCode;
9 |
10 | @JsonKey(name: "CASHRECEIPTFLAG")
11 | int cashReceiptFlag;
12 |
13 | Daou({
14 | this.productCode,
15 | required this.cashReceiptFlag,
16 | });
17 |
18 | factory Daou.fromJson(Map json) => _$DaouFromJson(json);
19 |
20 | Map toJson() => _$DaouToJson(this);
21 | }
22 |
--------------------------------------------------------------------------------
/lib/model/pg/daou/daou.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'daou.dart';
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | Daou _$DaouFromJson(Map json) => Daou(
10 | productCode: json['PRODUCTCODE'] as String?,
11 | cashReceiptFlag: json['CASHRECEIPTFLAG'] as int,
12 | );
13 |
14 | Map _$DaouToJson(Daou instance) {
15 | final val = {};
16 |
17 | void writeNotNull(String key, dynamic value) {
18 | if (value != null) {
19 | val[key] = value;
20 | }
21 | }
22 |
23 | writeNotNull('PRODUCTCODE', instance.productCode);
24 | val['CASHRECEIPTFLAG'] = instance.cashReceiptFlag;
25 | return val;
26 | }
27 |
--------------------------------------------------------------------------------
/lib/model/pg/kcp/kcp_products.dart:
--------------------------------------------------------------------------------
1 | import 'package:json_annotation/json_annotation.dart';
2 |
3 | part 'kcp_products.g.dart';
4 |
5 | @JsonSerializable()
6 | class KcpProducts {
7 | String orderNumber;
8 | String name;
9 | int quantity;
10 | int amount;
11 |
12 | KcpProducts({
13 | required this.orderNumber,
14 | required this.name,
15 | required this.quantity,
16 | required this.amount,
17 | });
18 |
19 | factory KcpProducts.fromJson(Map json) =>
20 | _$KcpProductsFromJson(json);
21 |
22 | Map toJson() => _$KcpProductsToJson(this);
23 | }
24 |
--------------------------------------------------------------------------------
/lib/model/pg/kcp/kcp_products.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'kcp_products.dart';
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | KcpProducts _$KcpProductsFromJson(Map json) => KcpProducts(
10 | orderNumber: json['orderNumber'] as String,
11 | name: json['name'] as String,
12 | quantity: json['quantity'] as int,
13 | amount: json['amount'] as int,
14 | );
15 |
16 | Map _$KcpProductsToJson(KcpProducts instance) =>
17 | {
18 | 'orderNumber': instance.orderNumber,
19 | 'name': instance.name,
20 | 'quantity': instance.quantity,
21 | 'amount': instance.amount,
22 | };
23 |
--------------------------------------------------------------------------------
/lib/model/pg/naver/naver_co_products.dart:
--------------------------------------------------------------------------------
1 | import 'package:portone_flutter/model/pg/naver/naver_products.dart';
2 | import 'package:json_annotation/json_annotation.dart';
3 |
4 | part 'naver_co_products.g.dart';
5 |
6 | @JsonSerializable()
7 | class NaverCoProducts implements NaverProducts {
8 | String? id;
9 | String? merchantProductId;
10 | String? ecMallProductId;
11 | String? name;
12 | int? basePrice;
13 | String? taxType;
14 | int? quantity;
15 | String? infoUrl;
16 | String? imageUrl;
17 | String? giftName;
18 | List? options;
19 | NaverCoShipping? shipping;
20 | List? supplements;
21 |
22 | NaverCoProducts({
23 | this.id,
24 | this.merchantProductId,
25 | this.ecMallProductId,
26 | this.name,
27 | this.basePrice,
28 | this.taxType,
29 | this.quantity,
30 | this.infoUrl,
31 | this.imageUrl,
32 | this.giftName,
33 | this.options,
34 | this.shipping,
35 | this.supplements,
36 | });
37 |
38 | factory NaverCoProducts.fromJson(Map json) =>
39 | _$NaverCoProductsFromJson(json);
40 |
41 | Map toJson() => _$NaverCoProductsToJson(this);
42 | }
43 |
44 | @JsonSerializable()
45 | class NaverCoOption {
46 | int? optionQuantity;
47 | int? optionPrice;
48 | String? selectionCode;
49 | List? selections;
50 |
51 | NaverCoOption({
52 | this.optionQuantity,
53 | this.optionPrice,
54 | this.selectionCode,
55 | this.selections,
56 | });
57 |
58 | factory NaverCoOption.fromJson(Map json) =>
59 | _$NaverCoOptionFromJson(json);
60 |
61 | Map toJson() => _$NaverCoOptionToJson(this);
62 | }
63 |
64 | @JsonSerializable()
65 | class NaverCoOptionItem {
66 | String? code;
67 | String? label;
68 | String? value;
69 |
70 | NaverCoOptionItem({
71 | this.code,
72 | this.label,
73 | this.value,
74 | });
75 |
76 | factory NaverCoOptionItem.fromJson(Map json) =>
77 | _$NaverCoOptionItemFromJson(json);
78 |
79 | Map toJson() => _$NaverCoOptionItemToJson(this);
80 | }
81 |
82 | @JsonSerializable()
83 | class NaverCoSupplement {
84 | String? id;
85 | String? name;
86 | int? price;
87 | int? quantity;
88 |
89 | NaverCoSupplement({
90 | this.id,
91 | this.name,
92 | this.price,
93 | this.quantity,
94 | });
95 |
96 | factory NaverCoSupplement.fromJson(Map json) =>
97 | _$NaverCoSupplementFromJson(json);
98 |
99 | Map toJson() => _$NaverCoSupplementToJson(this);
100 | }
101 |
102 | @JsonSerializable()
103 | class NaverCoShipping {
104 | String? groupId;
105 | String? method;
106 | int? baseFee;
107 | String? feePayType;
108 | NaverCoFeeRule? feeRule;
109 |
110 | NaverCoShipping({
111 | this.groupId,
112 | this.method,
113 | this.baseFee,
114 | this.feePayType,
115 | this.feeRule,
116 | });
117 |
118 | factory NaverCoShipping.fromJson(Map json) =>
119 | _$NaverCoShippingFromJson(json);
120 |
121 | Map toJson() => _$NaverCoShippingToJson(this);
122 | }
123 |
124 | @JsonSerializable()
125 | class NaverCoFeeRule {
126 | int? freeByThreshold;
127 | int? repeatByQty;
128 | List? rangesByQty;
129 | List? surchargesByArea;
130 |
131 | NaverCoFeeRule({
132 | this.freeByThreshold,
133 | this.repeatByQty,
134 | this.rangesByQty,
135 | this.surchargesByArea,
136 | });
137 |
138 | factory NaverCoFeeRule.fromJson(Map json) =>
139 | _$NaverCoFeeRuleFromJson(json);
140 |
141 | Map toJson() => _$NaverCoFeeRuleToJson(this);
142 | }
143 |
144 | @JsonSerializable()
145 | class NaverCoFeeRangeByQty {
146 | int? from;
147 | int? surcharge;
148 |
149 | NaverCoFeeRangeByQty({
150 | this.from,
151 | this.surcharge,
152 | });
153 |
154 | factory NaverCoFeeRangeByQty.fromJson(Map json) =>
155 | _$NaverCoFeeRangeByQtyFromJson(json);
156 |
157 | Map toJson() => _$NaverCoFeeRangeByQtyToJson(this);
158 | }
159 |
160 | @JsonSerializable()
161 | class NaverCoFeeAreaByQty {
162 | List? from;
163 | int? surcharge;
164 |
165 | NaverCoFeeAreaByQty({
166 | this.from,
167 | this.surcharge,
168 | });
169 |
170 | factory NaverCoFeeAreaByQty.fromJson(Map json) =>
171 | _$NaverCoFeeAreaByQtyFromJson(json);
172 |
173 | Map toJson() => _$NaverCoFeeAreaByQtyToJson(this);
174 | }
175 |
--------------------------------------------------------------------------------
/lib/model/pg/naver/naver_co_products.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'naver_co_products.dart';
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | NaverCoProducts _$NaverCoProductsFromJson(Map json) =>
10 | NaverCoProducts(
11 | id: json['id'] as String?,
12 | merchantProductId: json['merchantProductId'] as String?,
13 | ecMallProductId: json['ecMallProductId'] as String?,
14 | name: json['name'] as String?,
15 | basePrice: json['basePrice'] as int?,
16 | taxType: json['taxType'] as String?,
17 | quantity: json['quantity'] as int?,
18 | infoUrl: json['infoUrl'] as String?,
19 | imageUrl: json['imageUrl'] as String?,
20 | giftName: json['giftName'] as String?,
21 | options: (json['options'] as List?)
22 | ?.map((e) => NaverCoOption.fromJson(e as Map))
23 | .toList(),
24 | shipping: json['shipping'] == null
25 | ? null
26 | : NaverCoShipping.fromJson(json['shipping'] as Map),
27 | supplements: (json['supplements'] as List?)
28 | ?.map((e) => NaverCoSupplement.fromJson(e as Map))
29 | .toList(),
30 | );
31 |
32 | Map _$NaverCoProductsToJson(NaverCoProducts instance) {
33 | final val = {};
34 |
35 | void writeNotNull(String key, dynamic value) {
36 | if (value != null) {
37 | val[key] = value;
38 | }
39 | }
40 |
41 | writeNotNull('id', instance.id);
42 | writeNotNull('merchantProductId', instance.merchantProductId);
43 | writeNotNull('ecMallProductId', instance.ecMallProductId);
44 | writeNotNull('name', instance.name);
45 | writeNotNull('basePrice', instance.basePrice);
46 | writeNotNull('taxType', instance.taxType);
47 | writeNotNull('quantity', instance.quantity);
48 | writeNotNull('infoUrl', instance.infoUrl);
49 | writeNotNull('imageUrl', instance.imageUrl);
50 | writeNotNull('giftName', instance.giftName);
51 | writeNotNull('options', instance.options);
52 | writeNotNull('shipping', instance.shipping);
53 | writeNotNull('supplements', instance.supplements);
54 | return val;
55 | }
56 |
57 | NaverCoOption _$NaverCoOptionFromJson(Map json) =>
58 | NaverCoOption(
59 | optionQuantity: json['optionQuantity'] as int?,
60 | optionPrice: json['optionPrice'] as int?,
61 | selectionCode: json['selectionCode'] as String?,
62 | selections: (json['selections'] as List?)
63 | ?.map((e) => NaverCoOptionItem.fromJson(e as Map))
64 | .toList(),
65 | );
66 |
67 | Map _$NaverCoOptionToJson(NaverCoOption instance) {
68 | final val = {};
69 |
70 | void writeNotNull(String key, dynamic value) {
71 | if (value != null) {
72 | val[key] = value;
73 | }
74 | }
75 |
76 | writeNotNull('optionQuantity', instance.optionQuantity);
77 | writeNotNull('optionPrice', instance.optionPrice);
78 | writeNotNull('selectionCode', instance.selectionCode);
79 | writeNotNull('selections', instance.selections);
80 | return val;
81 | }
82 |
83 | NaverCoOptionItem _$NaverCoOptionItemFromJson(Map json) =>
84 | NaverCoOptionItem(
85 | code: json['code'] as String?,
86 | label: json['label'] as String?,
87 | value: json['value'] as String?,
88 | );
89 |
90 | Map _$NaverCoOptionItemToJson(NaverCoOptionItem instance) {
91 | final val = {};
92 |
93 | void writeNotNull(String key, dynamic value) {
94 | if (value != null) {
95 | val[key] = value;
96 | }
97 | }
98 |
99 | writeNotNull('code', instance.code);
100 | writeNotNull('label', instance.label);
101 | writeNotNull('value', instance.value);
102 | return val;
103 | }
104 |
105 | NaverCoSupplement _$NaverCoSupplementFromJson(Map json) =>
106 | NaverCoSupplement(
107 | id: json['id'] as String?,
108 | name: json['name'] as String?,
109 | price: json['price'] as int?,
110 | quantity: json['quantity'] as int?,
111 | );
112 |
113 | Map _$NaverCoSupplementToJson(NaverCoSupplement instance) {
114 | final val = {};
115 |
116 | void writeNotNull(String key, dynamic value) {
117 | if (value != null) {
118 | val[key] = value;
119 | }
120 | }
121 |
122 | writeNotNull('id', instance.id);
123 | writeNotNull('name', instance.name);
124 | writeNotNull('price', instance.price);
125 | writeNotNull('quantity', instance.quantity);
126 | return val;
127 | }
128 |
129 | NaverCoShipping _$NaverCoShippingFromJson(Map json) =>
130 | NaverCoShipping(
131 | groupId: json['groupId'] as String?,
132 | method: json['method'] as String?,
133 | baseFee: json['baseFee'] as int?,
134 | feePayType: json['feePayType'] as String?,
135 | feeRule: json['feeRule'] == null
136 | ? null
137 | : NaverCoFeeRule.fromJson(json['feeRule'] as Map),
138 | );
139 |
140 | Map _$NaverCoShippingToJson(NaverCoShipping instance) {
141 | final val = {};
142 |
143 | void writeNotNull(String key, dynamic value) {
144 | if (value != null) {
145 | val[key] = value;
146 | }
147 | }
148 |
149 | writeNotNull('groupId', instance.groupId);
150 | writeNotNull('method', instance.method);
151 | writeNotNull('baseFee', instance.baseFee);
152 | writeNotNull('feePayType', instance.feePayType);
153 | writeNotNull('feeRule', instance.feeRule);
154 | return val;
155 | }
156 |
157 | NaverCoFeeRule _$NaverCoFeeRuleFromJson(Map json) =>
158 | NaverCoFeeRule(
159 | freeByThreshold: json['freeByThreshold'] as int?,
160 | repeatByQty: json['repeatByQty'] as int?,
161 | rangesByQty: (json['rangesByQty'] as List?)
162 | ?.map((e) => NaverCoFeeRangeByQty.fromJson(e as Map))
163 | .toList(),
164 | surchargesByArea: (json['surchargesByArea'] as List?)
165 | ?.map((e) => NaverCoFeeAreaByQty.fromJson(e as Map))
166 | .toList(),
167 | );
168 |
169 | Map _$NaverCoFeeRuleToJson(NaverCoFeeRule instance) {
170 | final val = {};
171 |
172 | void writeNotNull(String key, dynamic value) {
173 | if (value != null) {
174 | val[key] = value;
175 | }
176 | }
177 |
178 | writeNotNull('freeByThreshold', instance.freeByThreshold);
179 | writeNotNull('repeatByQty', instance.repeatByQty);
180 | writeNotNull('rangesByQty', instance.rangesByQty);
181 | writeNotNull('surchargesByArea', instance.surchargesByArea);
182 | return val;
183 | }
184 |
185 | NaverCoFeeRangeByQty _$NaverCoFeeRangeByQtyFromJson(
186 | Map json) =>
187 | NaverCoFeeRangeByQty(
188 | from: json['from'] as int?,
189 | surcharge: json['surcharge'] as int?,
190 | );
191 |
192 | Map _$NaverCoFeeRangeByQtyToJson(
193 | NaverCoFeeRangeByQty instance) {
194 | final val = {};
195 |
196 | void writeNotNull(String key, dynamic value) {
197 | if (value != null) {
198 | val[key] = value;
199 | }
200 | }
201 |
202 | writeNotNull('from', instance.from);
203 | writeNotNull('surcharge', instance.surcharge);
204 | return val;
205 | }
206 |
207 | NaverCoFeeAreaByQty _$NaverCoFeeAreaByQtyFromJson(Map json) =>
208 | NaverCoFeeAreaByQty(
209 | from: (json['from'] as List?)?.map((e) => e as String).toList(),
210 | surcharge: json['surcharge'] as int?,
211 | );
212 |
213 | Map _$NaverCoFeeAreaByQtyToJson(NaverCoFeeAreaByQty instance) {
214 | final val = {};
215 |
216 | void writeNotNull(String key, dynamic value) {
217 | if (value != null) {
218 | val[key] = value;
219 | }
220 | }
221 |
222 | writeNotNull('from', instance.from);
223 | writeNotNull('surcharge', instance.surcharge);
224 | return val;
225 | }
226 |
--------------------------------------------------------------------------------
/lib/model/pg/naver/naver_interface.dart:
--------------------------------------------------------------------------------
1 | import 'package:json_annotation/json_annotation.dart';
2 |
3 | part 'naver_interface.g.dart';
4 |
5 | @JsonSerializable()
6 | class NaverInterface {
7 | String? cpaInflowCode;
8 | String? naverInflowCode;
9 | String? saClickId;
10 | String? merchantCustomCode1;
11 | String? merchantCustomCode2;
12 |
13 | NaverInterface({
14 | this.cpaInflowCode,
15 | this.naverInflowCode,
16 | this.saClickId,
17 | this.merchantCustomCode1,
18 | this.merchantCustomCode2,
19 | });
20 |
21 | factory NaverInterface.fromJson(Map json) =>
22 | _$NaverInterfaceFromJson(json);
23 |
24 | Map toJson() => _$NaverInterfaceToJson(this);
25 | }
26 |
--------------------------------------------------------------------------------
/lib/model/pg/naver/naver_interface.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'naver_interface.dart';
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | NaverInterface _$NaverInterfaceFromJson(Map json) =>
10 | NaverInterface(
11 | cpaInflowCode: json['cpaInflowCode'] as String?,
12 | naverInflowCode: json['naverInflowCode'] as String?,
13 | saClickId: json['saClickId'] as String?,
14 | merchantCustomCode1: json['merchantCustomCode1'] as String?,
15 | merchantCustomCode2: json['merchantCustomCode2'] as String?,
16 | );
17 |
18 | Map _$NaverInterfaceToJson(NaverInterface instance) {
19 | final val = {};
20 |
21 | void writeNotNull(String key, dynamic value) {
22 | if (value != null) {
23 | val[key] = value;
24 | }
25 | }
26 |
27 | writeNotNull('cpaInflowCode', instance.cpaInflowCode);
28 | writeNotNull('naverInflowCode', instance.naverInflowCode);
29 | writeNotNull('saClickId', instance.saClickId);
30 | writeNotNull('merchantCustomCode1', instance.merchantCustomCode1);
31 | writeNotNull('merchantCustomCode2', instance.merchantCustomCode2);
32 | return val;
33 | }
34 |
--------------------------------------------------------------------------------
/lib/model/pg/naver/naver_pay_products.dart:
--------------------------------------------------------------------------------
1 | import 'package:portone_flutter/model/pg/naver/naver_products.dart';
2 | import 'package:json_annotation/json_annotation.dart';
3 |
4 | part 'naver_pay_products.g.dart';
5 |
6 | @JsonSerializable()
7 | class NaverPayProducts implements NaverProducts {
8 | String categoryType;
9 | String categoryId;
10 | String uid;
11 | String name;
12 | int count;
13 | String? payReferrer;
14 |
15 | NaverPayProducts({
16 | required this.categoryType,
17 | required this.categoryId,
18 | required this.uid,
19 | required this.name,
20 | required this.count,
21 | this.payReferrer,
22 | });
23 |
24 | factory NaverPayProducts.fromJson(Map json) =>
25 | _$NaverPayProductsFromJson(json);
26 |
27 | Map toJson() => _$NaverPayProductsToJson(this);
28 | }
29 |
--------------------------------------------------------------------------------
/lib/model/pg/naver/naver_pay_products.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'naver_pay_products.dart';
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | NaverPayProducts _$NaverPayProductsFromJson(Map json) =>
10 | NaverPayProducts(
11 | categoryType: json['categoryType'] as String,
12 | categoryId: json['categoryId'] as String,
13 | uid: json['uid'] as String,
14 | name: json['name'] as String,
15 | count: json['count'] as int,
16 | payReferrer: json['payReferrer'] as String?,
17 | );
18 |
19 | Map _$NaverPayProductsToJson(NaverPayProducts instance) {
20 | final val = {
21 | 'categoryType': instance.categoryType,
22 | 'categoryId': instance.categoryId,
23 | 'uid': instance.uid,
24 | 'name': instance.name,
25 | 'count': instance.count,
26 | };
27 |
28 | void writeNotNull(String key, dynamic value) {
29 | if (value != null) {
30 | val[key] = value;
31 | }
32 | }
33 |
34 | writeNotNull('payReferrer', instance.payReferrer);
35 | return val;
36 | }
37 |
--------------------------------------------------------------------------------
/lib/model/pg/naver/naver_products.dart:
--------------------------------------------------------------------------------
1 | import 'package:portone_flutter/model/pg/naver/naver_co_products.dart';
2 | import 'package:portone_flutter/model/pg/naver/naver_pay_products.dart';
3 | import 'package:json_annotation/json_annotation.dart';
4 |
5 | part 'naver_products.g.dart';
6 |
7 | @JsonSerializable(createFactory: false)
8 | abstract class NaverProducts {
9 | NaverProducts();
10 |
11 | factory NaverProducts.fromJson(Map json) {
12 | if (json['id'] != null) {
13 | return NaverCoProducts.fromJson(json);
14 | } else {
15 | return NaverPayProducts.fromJson(json);
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/lib/model/pg/naver/naver_products.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'naver_products.dart';
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | Map _$NaverProductsToJson(NaverProducts instance) =>
10 | {};
11 |
--------------------------------------------------------------------------------
/lib/model/pg/settle/settle.dart:
--------------------------------------------------------------------------------
1 | import 'package:json_annotation/json_annotation.dart';
2 |
3 | part 'settle.g.dart';
4 |
5 | @JsonSerializable()
6 | class Settle {
7 | @JsonKey(name: "criPsblYn") // bypass.settle.criPsblYn
8 | String? cashReceiptType; // Y or N (default "Y")
9 |
10 | Settle({
11 | this.cashReceiptType,
12 | });
13 |
14 | factory Settle.fromJson(Map json) => _$SettleFromJson(json);
15 |
16 | Map toJson() => _$SettleToJson(this);
17 | }
18 |
--------------------------------------------------------------------------------
/lib/model/pg/settle/settle.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'settle.dart';
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | Settle _$SettleFromJson(Map json) => Settle(
10 | cashReceiptType: json['criPsblYn'] as String?,
11 | );
12 |
13 | Map _$SettleToJson(Settle instance) {
14 | final val = {};
15 |
16 | void writeNotNull(String key, dynamic value) {
17 | if (value != null) {
18 | val[key] = value;
19 | }
20 | }
21 |
22 | writeNotNull('criPsblYn', instance.cashReceiptType);
23 | return val;
24 | }
25 |
--------------------------------------------------------------------------------
/lib/model/pg/tosspayments/tosspayments.dart:
--------------------------------------------------------------------------------
1 | import 'package:json_annotation/json_annotation.dart';
2 |
3 | part 'tosspayments.g.dart';
4 |
5 | @JsonSerializable()
6 | class Tosspayments {
7 | bool? useInternationalCardOnly;
8 | String? discountCode;
9 |
10 | Tosspayments({
11 | this.useInternationalCardOnly,
12 | this.discountCode,
13 | });
14 |
15 | factory Tosspayments.fromJson(Map json) =>
16 | _$TosspaymentsFromJson(json);
17 |
18 | Map toJson() => _$TosspaymentsToJson(this);
19 | }
20 |
--------------------------------------------------------------------------------
/lib/model/pg/tosspayments/tosspayments.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'tosspayments.dart';
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | Tosspayments _$TosspaymentsFromJson(Map json) => Tosspayments(
10 | useInternationalCardOnly: json['useInternationalCardOnly'] as bool?,
11 | discountCode: json['discountCode'] as String?,
12 | );
13 |
14 | Map _$TosspaymentsToJson(Tosspayments instance) {
15 | final val = {};
16 |
17 | void writeNotNull(String key, dynamic value) {
18 | if (value != null) {
19 | val[key] = value;
20 | }
21 | }
22 |
23 | writeNotNull('useInternationalCardOnly', instance.useInternationalCardOnly);
24 | writeNotNull('discountCode', instance.discountCode);
25 | return val;
26 | }
27 |
--------------------------------------------------------------------------------
/lib/model/url_data.dart:
--------------------------------------------------------------------------------
1 | class UrlData {
2 | static const String redirectUrl =
3 | 'http://detectchangingwebview/iamport/f'; // f는 flutter의 f
4 | static const String ANDROID_MARKET_PREFIX = 'market://details?id=';
5 | static const String IOS_MARKET_PREFIX = 'itms-apps://itunes.apple.com/app/';
6 |
7 | static const String ISP = 'ispmobile';
8 | static const String PACKAGE_ISP = 'kvp.jjy.MispAndroid320';
9 |
10 | static const String BANKPAY = 'kftc-bankpay';
11 | static const String PACKAGE_BANKPAY = 'com.kftc.bankpay.android';
12 |
13 | static const String KB_BANKPAY = 'kb-bankpay';
14 | static const String PACKAGE_KB_BANKPAY = 'com.kbstar.liivbank';
15 |
16 | static const String NH_BANKPAY = 'nhb-bankpay';
17 | static const String PACKAGE_NH_BANKPAY = 'com.nh.cashcardapp';
18 |
19 | static const String MG_BANKPAY = 'mg-bankpay';
20 | static const String PACKAGE_MG_BANKPAY = 'kr.co.kfcc.mobilebank';
21 |
22 | static const String KN_BANKPAY = 'kn-bankpay';
23 | static const String PACKAGE_KN_BANKPAY = 'com.knb.psb';
24 |
25 | static const String KAKAOPAY = 'kakaotalk';
26 | static const String PACKAGE_KAKAOPAY = 'com.kakao.talk';
27 |
28 | static const String SMILEPAY = 'smilepayapp';
29 | static const String PACKAGE_SMILEPAY = 'com.mysmilepay.app';
30 | static const String SMILEPAY_BASE_URL = "https://www.mysmilepay.com/";
31 |
32 | static const String CHAIPAY = 'chaipayment';
33 | static const String PACKAGE_CHAIPAY = 'finance.chai.app';
34 |
35 | static const String PAYCO = 'payco';
36 | static const String PACKAGE_PAYCO = 'com.nhnent.payapp';
37 |
38 | static const String HYUNDAICARD = 'hdcardappcardansimclick';
39 | static const String PACKAGE_HYUNDAICARD = 'com.hyundaicard.appcard';
40 |
41 | static const String TOSS = 'supertoss';
42 | static const String PACKAGE_TOSS = 'viva.republica.toss';
43 |
44 | static const String SHINHANCARD = 'shinhan-sr-ansimclick';
45 | static const String PACKAGE_SHINHANCARD = 'com.shcard.smartpay';
46 |
47 | static const String HANACARD = 'cloudpay';
48 | static const String PACKAGE_HANACARD = 'com.hanaskcard.paycla';
49 |
50 | static const String SAMSUNGCARD = 'mpocket.online.ansimclick';
51 | static const String PACKAGE_SAMSUNGCARD = 'kr.co.samsungcard.mpocket';
52 |
53 | static const String KBCARD = 'kb-acp';
54 | static const String PACKAGE_KBCARD = 'com.kbcard.cxh.appcard';
55 |
56 | static const String NHCARD = 'nhallonepayansimclick';
57 | static const String PACKAGE_NHCARD = 'nh.smart.nhallonepay';
58 |
59 | static const String CITICARD = 'citimobileapp';
60 | static const String PACKAGE_CITICARD = 'kr.co.citibank.citimobile';
61 |
62 | static const String LOTTECARD = 'lotteappcard';
63 | static const String PACKAGE_LOTTECARD = 'com.lcacApp';
64 |
65 | static const String LPAY = 'lpayapp';
66 | static const String PACKAGE_LPAY = 'com.lotte.lpay';
67 |
68 | static const String SSGPAY = 'shinsegaeeasypayment';
69 | static const String PACKAGE_SSGPAY =
70 | 'com.ssg.serviceapp.android.egiftcertificate';
71 |
72 | static const String KPAY = 'kpay';
73 | static const String PACKAGE_KPAY = 'com.inicis.kpay';
74 |
75 | static const String PAYNOW = 'lguthepay-xpay';
76 | static const String PACKAGE_PAYNOW = 'com.lguplus.paynow';
77 |
78 | static const String WOORIWONCARD = 'com.wooricard.smartapp';
79 | static const String PACKAGE_WOORIWONCARD = 'com.wooricard.smartapp';
80 |
81 | static const String LPOINT = 'lmslpay';
82 | static const String PACKAGE_LPOINT = 'com.lottemembers.android';
83 |
84 | static const String WOORIWONBANK = 'wooribank';
85 | static const String PACKAGE_WOORIWONBANK = 'com.wooribank.smart.npib';
86 |
87 | static const String KTFAUTH = 'ktauthexternalcall';
88 | static const String PACKAGE_KTFAUTH = 'com.kt.ktauth';
89 |
90 | static const String LGTAUTH = 'upluscorporation';
91 | static const String PACKAGE_LGTAUTH = 'com.lguplus.smartotp';
92 |
93 | static const String SKTAUTH = 'tauthlink';
94 | static const String PACKAGE_SKTAUTH = 'com.sktelecom.tauth';
95 |
96 | static const String V3_MOBILE_PLUS = 'v3mobileplusweb';
97 | static const String PACKAGE_V3_MOBILE_PLUS = 'com.ahnlab.v3mobileplus';
98 |
99 | static const String KBBANK = 'kbbank';
100 | static const String PACKAGE_KBBANK = 'com.kbstar.kbkank';
101 |
102 | static const String LIIV_NEXT = 'newliiv';
103 | static const String PACKAGE_LIIV_NEXT = 'com.kbstar.reboot';
104 |
105 | static const String NAVER = 'nidlogin';
106 | static const String PACKAGE_NAVER = 'com.nhn.android.search';
107 | }
108 |
--------------------------------------------------------------------------------
/lib/widget/iamport_error.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:portone_flutter/widget/iamport_webview.dart';
3 |
4 | class IamportError extends StatelessWidget {
5 | static final Color failureColor = Color(0xfff5222d);
6 |
7 | final ActionType actionType;
8 | final String? errorMessage;
9 |
10 | IamportError(this.actionType, this.errorMessage);
11 |
12 | @override
13 | Widget build(BuildContext context) {
14 | String? actionText;
15 | if (actionType == ActionType.auth) {
16 | actionText = '본인인증';
17 | } else if (actionType == ActionType.payment) {
18 | actionText = '결제';
19 | }
20 |
21 | return Scaffold(
22 | appBar: AppBar(
23 | title: Text('포트원 V1 $actionText 결과'),
24 | centerTitle: true,
25 | automaticallyImplyLeading: false,
26 | ),
27 | body: SafeArea(
28 | child: Row(
29 | mainAxisAlignment: MainAxisAlignment.center,
30 | children: [
31 | Flexible(
32 | child: Column(
33 | mainAxisAlignment: MainAxisAlignment.center,
34 | crossAxisAlignment: CrossAxisAlignment.center,
35 | children: [
36 | Icon(
37 | Icons.error,
38 | color: failureColor,
39 | size: 200,
40 | ),
41 | Container(
42 | padding: EdgeInsets.symmetric(vertical: 10),
43 | child: Text(
44 | '포트원 V1 $actionText 파라메터 오류',
45 | style: TextStyle(
46 | fontWeight: FontWeight.bold,
47 | fontSize: 20,
48 | ),
49 | ),
50 | ),
51 | Container(
52 | padding: EdgeInsets.fromLTRB(50, 10, 50, 50),
53 | child: Text(
54 | errorMessage!,
55 | style: TextStyle(
56 | height: 1.2,
57 | fontSize: 16,
58 | ),
59 | textAlign: TextAlign.center,
60 | ),
61 | ),
62 | ElevatedButton.icon(
63 | icon: Icon(Icons.arrow_back),
64 | onPressed: () {
65 | Navigator.pop(context);
66 | },
67 | label: Text(
68 | '돌아가기',
69 | style: TextStyle(fontSize: 16, color: failureColor),
70 | ),
71 | style: ElevatedButton.styleFrom(
72 | elevation: 0,
73 | shadowColor: Colors.transparent,
74 | ),
75 | ),
76 | ],
77 | ),
78 | ),
79 | ],
80 | ),
81 | ),
82 | );
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/lib/widget/iamport_webview.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:io';
3 |
4 | import 'package:flutter/foundation.dart';
5 | import 'package:flutter/gestures.dart';
6 | import 'package:flutter/material.dart';
7 | import 'package:portone_flutter/model/iamport_url.dart';
8 | import 'package:iamport_webview_flutter/iamport_webview_flutter.dart';
9 |
10 | enum ActionType { auth, payment }
11 |
12 | class IamportWebView extends StatefulWidget {
13 | static final Color primaryColor = Color(0xff344e81);
14 | static final String html = '''
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | ''';
25 |
26 | final ActionType type;
27 | final PreferredSizeWidget? appBar;
28 | final Widget? initialChild;
29 | final ValueSetter executeJS;
30 | final ValueSetter