카카오페이 단건결제 에러

참고자료 활용해서 카카오페이 단건결제를 테스트모드로 진행중입니다.

실행하면 카카오페이 결제 화면에서 "다음"버튼을 누르면 이런 에러 메시지가 뜨는데 어떻게 수정해야할까요

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; // :white_check_mark: 테스트 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(“:white_check_mark: _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(
:receipt: 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(“:round_pushpin: 버튼 클릭됨”); // 로그로 클릭 여부 확인
_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>

인용구

안녕하세요. 카카오페이입니다.

모바일의 경우 next_redirect_app_url or next_redirect_mobile_url 를 호출해야합니다.

다음버튼을 눌럿을때 동작해야하는 정보값은 카카오톡 스킴이 호출이 되어야합니다.(kakaotalk://kakaopay/pg?url=%s)

intent 관련 예시를 보내드리오니 참고하여 카톡 호출이 정상적일수 있도록 체크 부탁드립니다.

카카오톡 앱스킴 (intent://kakaopay/pg?url=%s#Intent;scheme=kakaotalk;package=com.kakao.talk;end)

sample
Intent intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME); //IntentURI처리
Uri uri = Uri.parse(intent.getDataString());
activity.startActivity(new Intent(Intent.ACTION_VIEW, uri));