참고자료 활용해서 카카오페이 단건결제를 테스트모드로 진행중입니다.
실행하면 카카오페이 결제 화면에서 "다음"버튼을 누르면 이런 에러 메시지가 뜨는데 어떻게 수정해야할까요
intent://kakaopay/pg?url=https://online-pay.kakaopay.com/pay/mockup/3562992dba50f3019b89 의 웹페이지를 로드할 수 없습니다.
shop_screen.dart
import ‘package:flutter/material.dart’;
import ‘package:flutter_inappwebview/flutter_inappwebview.dart’;
import ‘package:http/http.dart’ as http;
import ‘dart:convert’;
import ‘package:url_launcher/url_launcher.dart’;
class ShopScreen extends StatefulWidget {
const ShopScreen({Key? key}) : super(key: key);
@override
State createState() => _ShopScreenState();
}
class _ShopScreenState extends State {
String? _tid;
bool isTestMode = true; //
테스트 CID 사용 여부 설정
@override
void initState() {
super.initState();
}
Future<String?> _requestKakaoPay() async {
final url =
Uri.parse(“https://open-api.kakaopay.com/online/v1/payment/ready”);
final bodyData = {
"cid": isTestMode ? "TC0ONETIME" : "YOUR_REAL_CID",
"partner_order_id": "order1234",
"partner_user_id": "user1234",
"item_name": "강아지옷",
"quantity": "1",
"total_amount": "12000",
"tax_free_amount": "0",
"approval_url": "https://bellygomzzang.web.app/approve.html",
"cancel_url": "https://bellygomzzang.web.app/cancel.html",
"fail_url": "https://bellygomzzang.web.app/fail.html"
};
final response = await http.post(
url,
headers: {
"Authorization": "SECRET_KEY DEVA6771DB771F637FFA69EA7C68FDFE0441FD",
"Content-Type": "application/json",
},
body: json.encode(bodyData),
);
if (response.statusCode == 200) {
final data = json.decode(response.body);
print("✅ response data ${data}");
final redirectMobileUrl = data['next_redirect_mobile_url'];
_tid = data['tid'];
_openWebView(redirectMobileUrl);
} else {
print("❌ 카카오페이 오류: ${response.body}");
}
return null;
}
void _openWebView(String url) {
print(“
_openWebView url $url”);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Scaffold(
appBar: AppBar(title: Text("카카오페이 결제")),
body: InAppWebView(
initialUrlRequest: URLRequest(url: WebUri(url)),
onLoadStart: (controller, url) {
print("🌍 onLoadStart URL: $url");
},
shouldOverrideUrlLoading: (controller, navigationAction) async {
final uri = navigationAction.request.url;
if (uri == null) return NavigationActionPolicy.ALLOW;
final urlStr = uri.toString();
print("🔍 Intercepted URL: $urlStr");
// ✅ kakaotalk:// 같은 앱 스킴 감지
if (urlStr.startsWith("kakaotalk://")) {
try {
// launch() 대신 launchUrl() 사용
final uri = Uri.parse(urlStr);
if (await canLaunchUrl(uri)) {
await launchUrl(uri, mode: LaunchMode.externalApplication);
return NavigationActionPolicy.CANCEL;
} else {
print("❌ 카카오톡 앱 실행 실패");
}
} catch (e) {
print("❌ intent 처리 오류: $e");
}
}
return NavigationActionPolicy.ALLOW;
},
onLoadStop: (controller, uri) {
if (uri == null) return;
final uriStr = uri.toString();
print("🛰️ onLoadStop URL: $uriStr");
if (uri.queryParameters.containsKey("pg_token")) {
final pgToken = uri.queryParameters["pg_token"]!;
print("✅ pg_token 감지됨: $pgToken");
Navigator.pop(context);
_approveKakaoPay(pgToken);
}
},
),
),
),
);
}
Future _approveKakaoPay(String pgToken) async {
final url =
Uri.parse(“https://open-api.kakaopay.com/online/v1/payment/approve”);
print(
“
Approve Params: cid=${isTestMode ? “TC0ONETIME” : “REAL”}, tid=$_tid, pg_token=$pgToken”);
final response = await http.post(
url,
headers: {
"Authorization": "SECRET_KEY SECRET_KEY DEVA6771DB771F637FFA69EA7C68FDFE0441FD",
"Content-Type": "application/json",
},
body: json.encode({
"cid": isTestMode ? "TC0ONETIME" : "YOUR_REAL_CID",
"tid": _tid!,
"partner_order_id": "order1234",
"partner_user_id": "user1234",
"pg_token": pgToken,
}),
);
if (response.statusCode == 200) {
print("🎉 결제 승인 완료: ${response.body}");
_showResultDialog("결제 성공");
} else {
print("❌ 결제 승인 실패: ${response.body}");
_showResultDialog("결제 실패");
}
}
void showResultDialog(String message) {
showDialog(
context: context,
builder: () => AlertDialog(
title: Text(message),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text(“확인”),
),
],
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ElevatedButton(
onPressed: () {
print(“
버튼 클릭됨”); // 로그로 클릭 여부 확인
_requestKakaoPay(); // 실제 결제 요청 함수 호출
},
child: Text(“카카오페이 결제하기”),
),
),
);
}
}
AndroidMenifest.xml
<!-- 외부 앱 실행 허용 (예: kakaotalk) -->
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="kakaotalk" />
</intent>
<intent>
<action android:name="android.intent.action.PROCESS_TEXT"/>
<data android:mimeType="text/plain"/>
</intent>
</queries>
<application
android:label="bellygom"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:taskAffinity=""
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- 기본 테마 -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme" />
<!-- 앱 실행 인텐트 -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<!-- 딥링크 처리 인텐트 (howareyou://) -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="bellygom" />
</intent-filter>
</activity>
<!-- Flutter 설정 -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
인용구
