Back to blog
Engineering

How do I integrate payments in a mobile app?

How to integrate payments in a mobile app using WebView

Hecto Financial Engineering
2025-11-21
12 min read
#WebView#Android#iOS
How do I integrate payments in a mobile app? 썸네일 이미지

"I tapped Pay — nothing happened."

It's one of the most common pain points when integrating a PG into a mobile app.

"I tapped the ISP payment button and nothing happens." "Tapping KakaoPay doesn't open the app."

Payments that work fine in a desktop browser and mobile Chrome break silently inside an in-app WebView. The code is identical — so why does WebView behave differently?

The culprit is how App Schemes are handled.

This article covers why payments break in WebView and exactly what you need to fix it.

What is an App Scheme?

http:// and https:// tell the browser to open a web page. Addresses like ispmobile:// or kakaotalk:// mean something different: "launch this specific app." These are called App Schemes.

  • Mobile browsers (Chrome, Safari): When they encounter ispmobile://, they automatically recognize it as an app launch instruction and open the corresponding app.
  • WebView: By default, it only knows how to handle http and https. When it sees an unfamiliar scheme like ispmobile://, it throws an error and ignores it.

You need to explicitly tell WebView how to handle these URLs.


Android Setup

On Android, you need to configure app query permissions, URL handling, and core WebView settings together.

1. Permissions (AndroidManifest.xml)

Starting with Android 11 (API 30), if your app needs to launch other apps (card issuer apps, etc.), you must declare them upfront. Register the package names for apps your payment flow will call inside the <queries> tag. Refer to the App Scheme list at the bottom of this article for the package names you need.

<!-- AndroidManifest.xml -->
<manifest ...>
    <queries>
        <!-- KakaoTalk -->
        <package android:name="com.kakao.talk" />
        <!-- ISP/PayBook -->
        <package android:name="kvp.jjy.MispAndroid320" />
        <!-- Add package names for the card issuers / banks your integration uses -->
    </queries>
</manifest>

2. Implementation (WebViewClient)

Override shouldOverrideUrlLoading to intercept non-http/https URLs and launch the corresponding app via Intent. If you already have a WebViewClient, merge this logic into your existing shouldOverrideUrlLoading implementation rather than setting a new one.

import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.text.TextUtils;
import android.webkit.URLUtil;
import android.webkit.WebResourceRequest;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import java.net.URISyntaxException;

// Add external app launch URL handling to your WebViewClient.
webView.setWebViewClient(new WebViewClient() {

    // URL handling method called on Android N (API 24) and above.
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
        String url = request.getUrl().toString();
        return handlePaymentUrl(view, url);
    }

    // Implement this as well if you need to support devices below Android N (API 24).
    @SuppressWarnings("deprecation")
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
            return handlePaymentUrl(view, url);
        }

        return super.shouldOverrideUrlLoading(view, url);
    }

    private boolean handlePaymentUrl(WebView view, String url) {
        if (TextUtils.isEmpty(url)) {
            return false;
        }

        // Return false for http, https, and javascript URLs so WebView handles them normally.
        if (URLUtil.isNetworkUrl(url) || URLUtil.isJavaScriptUrl(url)) {
            return false;
        }

        final Uri uri;

        try {
            uri = Uri.parse(url);
        } catch (Exception e) {
            return false;
        }

        // Parse and launch intent:// URLs according to the Android Intent URI spec.
        if ("intent".equals(uri.getScheme())) {
            final Intent intent;

            try {
                intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
            } catch (URISyntaxException e) {
                return false;
            }

            try {
                view.getContext().startActivity(intent);
                return true;
            } catch (ActivityNotFoundException e) {
                // If the intent URL includes a package and the app isn't installed, redirect to the Play Store.
                String packageName = intent.getPackage();

                if (!TextUtils.isEmpty(packageName)) {
                    try {
                        Intent marketIntent = new Intent(
                            Intent.ACTION_VIEW,
                            Uri.parse("market://details?id=" + packageName)
                        );
                        view.getContext().startActivity(marketIntent);
                        return true;
                    } catch (Exception marketException) {
                        return false;
                    }
                }

                return false;
            } catch (Exception e) {
                return false;
            }
        }

        // Forward standard App Scheme URLs (ispmobile://, kftc-bankpay://, etc.) to the appropriate app.
        try {
            Intent schemeIntent = new Intent(Intent.ACTION_VIEW, uri);
            view.getContext().startActivity(schemeIntent);
            return true;
        } catch (Exception e) {
            // The app is not installed or no Activity can handle this URL.
            // If you need to redirect to the Play Store, use the Package Name from the App Scheme list below.
            return false;
        }
    }
});

Caution

If you're seeing net::ERR_UNKNOWN_URL_SCHEME, this code is not running correctly.

3. Core WebView Settings

Payment windows frequently rely on JavaScript, local storage, and cookies. Verify these WebView settings before loading the payment window.

import android.os.Build;
import android.webkit.CookieManager;
import android.webkit.WebSettings;

WebSettings settings = webView.getSettings();

// Allow JavaScript so payment window scripts can execute.
settings.setJavaScriptEnabled(true);

// Enable DOM Storage so the payment window can use Local Storage.
settings.setDomStorageEnabled(true);

// Use the default cache policy.
settings.setCacheMode(WebSettings.LOAD_DEFAULT);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    // Allow mixed content in case the payment/authentication flow loads http resources.
    settings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);

    // Allow cookies needed during the payment authentication flow.
    CookieManager cookieManager = CookieManager.getInstance();
    cookieManager.setAcceptCookie(true);
    cookieManager.setAcceptThirdPartyCookies(webView, true);
}

If the payment window uses alert or confirm, configure WebChromeClient so your app can display those dialogs natively. If you already have a WebChromeClient, merge the following logic into your existing implementation.

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebView;

webView.setWebChromeClient(new WebChromeClient() {

    // Display JavaScript alert() calls as a native dialog.
    @Override
    public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
        new AlertDialog.Builder(view.getContext())
            .setMessage(message)
            .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    result.confirm();
                }
            })
            .setCancelable(false)
            .create()
            .show();
        return true;
    }

    // Pass the user's confirm/cancel choice back to the payment window for JavaScript confirm() calls.
    @Override
    public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) {
        new AlertDialog.Builder(view.getContext())
            .setMessage(message)
            .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    result.confirm();
                }
            })
            .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    result.cancel();
                }
            })
            .create()
            .show();
        return true;
    }
});

iOS Setup

iOS follows the same pattern: register an allowlist, implement the delegate callback, and verify your cookie policy.

1. Allowlist (Info.plist)

Register every app scheme you need to open in the LSApplicationQueriesSchemes key. App scheme lists can change as card issuers and payment apps update their policies, so cross-reference the list at the bottom of this article.

<!-- Info.plist -->
<key>LSApplicationQueriesSchemes</key>
<array>
    <string>kakaotalk</string> <!-- KakaoTalk -->
    <string>ispmobile</string> <!-- ISP -->
    <string>kb-acp</string>    <!-- KB Kookmin Card -->
    <!-- Add other card issuer schemes -->
</array>

Caution

Any scheme not on this list will cause canOpenURL to return false, even if the app is installed.

2. Implementation (WKNavigationDelegate)

Intercept non-http/https schemes in decidePolicyFor and route them to the appropriate app.

// ViewController.swift
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
    if let url = navigationAction.request.url {
        // Non-http/https URLs are app schemes
        if url.scheme != "http" && url.scheme != "https" {
            // Check if the app is installed and open it
            if UIApplication.shared.canOpenURL(url) {
                UIApplication.shared.open(url)
                decisionHandler(.cancel) // Cancel WebView navigation
                return
            }
        }
    }
    decisionHandler(.allow) // Allow normal web page navigation
}

On iOS 6 and later, Safari's cookie settings can cause session errors during payment authentication. Set your app to always accept cookies at launch if needed.

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [[NSHTTPCookieStorage sharedHTTPCookieStorage]
        setCookieAcceptPolicy:NSHTTPCookieAcceptPolicyAlways];

    return YES;
}

App Scheme Reference

Common schemes for financial and payment apps. Use this list when registering entries in LSApplicationQueriesSchemes (iOS Info.plist) and <queries> (Android AndroidManifest.xml). Package Names are used on Android when redirecting to the Play Store for uninstalled apps.

AOS AppScheme

AppSchemePackage Name
ISP Mobileispmobilekvp.jjy.MispAndroid320
KB APP Cardkb-acpcom.kbcard.cxh.appcard
KB Star Bankingkbbankcom.kbstar.kbbank
L.POINTcom.lottemembers.android
L.paylpayappcom.lotte.lpay
LG Paycallonlinepaycom.lge.lgpay
LiiV (KB Kookmin Bank)liivbankcom.kbstar.liivbank
NH APP Cardnhappcardansimclicknh.smart.mobilecard
NH All One Paynhallonepayansimclicknh.smart.nhallonepay
PAYCOpaycocom.nhnent.payapp
SSGPAYcom.ssg.serviceapp.android.egiftcertificate
V3ahnlabv3mobilepluscom.ahnlab.v3mobileplus
VG Web Vaccinekr.co.shiftworks.vguardweb
mVaccinemvaccinestartcom.TouchEn.mVaccine.webs
Bank Transferkftc-bankpaycom.kftc.bankpay.android
Naver Paycom.nhn.android.search
Lotte APP Cardlotteappcardcom.lcacApp
Lotte Smart Paylottesmartpaycom.lotte.lottesmartpay
Liiv Nextnewliivcom.kbstar.reboot
Samsung APP Cardmpocket.online.ansimclickkr.co.samsungcard.mpocket
Samsung Paysamsungpaycom.samsung.android.spay
Samsung Pay (Mini)com.samsung.android.spaylite
Monimo Paynet.ib.android.smcard
Shinhan PayFan (Certificate)com.shinhancard.smartshinhan
Shinhan SOL Bankcom.shinhan.sbanking
Shinhan APP Cardshinhan-sr-ansimclickcom.shcard.smartpay
Shinhan Super SOLcom.shinhan.smartcaremgr
Shinhan Card Travel Walletcom.mobiletoong.travelwallet
Citi (Certificate / Simple Pay)smartpaykr.co.citibank.citimobile
Citi Mobile Appcitimobilekr.co.citibank.citimobile
Citi App Certificateciticardappcom.citibank.cardapp
Citi App Simple Paycitispaycom.citibank.cardapp
Woori WON Bankingwooribankcom.wooribank.smart.npib
Woori WON Cardcom.wooricard.smartappcom.wooricard.smartapp
Woori App Cardwooripaycom.wooricard.wpay
KakaoPaycom.kakao.talk
Kona Gimpo Paygov.gimpo.gpay
Tosssupertossviva.republica.toss
T-money Damdamcom.tmoney.nfc_pay
T-money In-Appcom.tmoney.inapp
PayPinpaypincom.skp.android.paypin
Hana (MobiPay)cloudpaycom.hanaskcard.paycla
Hana Cardcom.hanaskcard.rocomo.potal
Hana Memberskr.co.hanamembers.hmscustomer
Hana Members Wallethanawalletmemberscom.hanaskcard.paycla
Hyundai APP Cardhdcardappcardansimclickcom.hyundaicard.appcard
Hyundai Card (Certificate)com.lumensoft.touchenappfree
Pay Nowcom.lguplus.paynow
Kakao Bankkakaobankcom.kakaobank.channel

iOS AppScheme

AppScheme
ISP Mobileispmobile
KB APP Cardkb-acp
LiiV (KB Kookmin Bank)liivbank
Liiv Nextnewliiv
KB Star Bankingkbbank
Lotte APP Cardlotteappcard
Lotte Smart Paylottesmartpay
Monimo Paymonimopay
Monimo Pay Authmonimopayauth
Hyundai APP Cardhdcardappcardansimclick
Hyundai Certificate Appsmhyundaiansimclick
Samsung APP Cardmpocket.online.ansimclick
Samsung Card AnsimClickansimclickscard
ISP Collectionansimclickipcollect
V-Guardvguardstart
Samsung Paysamsungpay
Samsung Certificate Appscardcertiapp
Shinhan APP Cardshinhan-sr-ansimclick
Shinhan Certificate Appsmshinhanansimclick
NH APP Cardnhappcardansimclick
NH All One Paynhallonepayansimclick
NH Certificate Appnonghyupcardansimclick
Hana (MobiPay)cloudpay
Citi APP Cardcitispay
Citi Certificate Appciticardappkr
Citi Certificate / Simple Pay (New)citimobileapp
mVaccineNA
Bank Transferkftc-bankpay
KB Bank Transferkb-bankpay
PayPinpaypin
PAYCOpayco, paycoapplogin (both required)
Syrup APP Cardtswansimclick
Bank Walletbankwallet
UnionPayuppay
Hana Cardhanaskcardmobileportal
LG PayCallonlinepay
L.paylpayapp
L.pay LMSlmslpay
Woori App Cardwooripay
Hana Members Wallethanawalletmembers
Woori WON Cardcom.wooricard.wcard
Hana Moa Signhanamopmoasign
Woori WON Bankingnewsmartpib
Liiv (KB Kookmin Bank)liivbank
Shinsegae Easy Payshinsegaeeasypayment
T Authenticationtauthlink
KT Authenticationktauthexternalcall
U+ Authenticationupluscorporation
KakaoTalkkakaotalk
Naver Paynaversearchthirdlogin
Tosssupertoss
Kakao Bankkakaobank

Three things to get right for WebView payments

WebView isn't as smart as a browser — you have to spell everything out.

  1. Android: Register packages in AndroidManifest.xml + implement shouldOverrideUrlLoading
  2. iOS: Register schemes in Info.plist + implement decidePolicyFor
  3. Both: Know the Scheme and Package Name for every card issuer and bank your integration needs, and register them upfront

Get these three right, and your WebView payment experience will be just as smooth as the browser.

For questions during integration, contact our technical support team at .

💬

Need technical support?