PAYATHON 2026

如何在 Android WebView 中完成 Prestashop 支付?

支付阿杰

结论

Android WebView 可以完成 PrestaShop 结账,但 App 不应自行构造或解密 PrestaShop 的 session cookie。

通常有两种可靠的实现方式:

  1. 用户直接在 WebView 中登录、加购并结账,PrestaShop 通过 Set-Cookie 正常建立会话。
  2. 如果购物车来自原生 App,则由业务服务端创建或关联购物车,并生成一次性结账链接。WebView 打开链接后,服务端建立 PrestaShop 会话,再跳转到结账页。

对于“原生商品页、WebView 支付”这类混合模式,第二种方式更合适。

PrestaShop 的 Cookie 并不只是购物车 ID。它通常包含客户、购物车、语言、店铺等会话信息,并使用店铺密钥签名或加密。具体字段和处理方式也可能随 PrestaShop 版本、配置及模块变化。

如果把加密密钥放进 Android 安装包,不仅有泄露风险,客户端生成的 Cookie 还容易遇到这些问题:

  • Cookie 格式与当前 PrestaShop 版本不一致;
  • 域名、路径、Secure、SameSite 等属性不匹配;
  • 客户与购物车无法正确关联;
  • 店铺密钥泄露后,攻击者可能伪造其他会话;
  • PrestaShop 升级后,原有实现可能失效。

因此,Cookie 应始终由 PrestaShop 或受信任的服务端生成。

方案一:全部操作都在 WebView 中完成

如果用户可以直接在网页中登录和加购,实现会比较简单。开启 Cookie、JavaScript 和 DOM Storage 后,打开店铺页面即可。

@SuppressLint("SetJavaScriptEnabled")
fun configureCheckoutWebView(webView: WebView, checkoutUrl: String) {
    val cookieManager = CookieManager.getInstance()
    cookieManager.setAcceptCookie(true)

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        cookieManager.setAcceptThirdPartyCookies(webView, true)
    }

    webView.settings.apply {
        javaScriptEnabled = true
        domStorageEnabled = true
        javaScriptCanOpenWindowsAutomatically = false
        mixedContentMode = WebSettings.MIXED_CONTENT_NEVER_ALLOW
    }

    webView.webViewClient = object : WebViewClient() {
        override fun shouldOverrideUrlLoading(
            view: WebView,
            request: WebResourceRequest
        ): Boolean {
            val uri = request.url

            return when (uri.scheme?.lowercase()) {
                "http", "https" -> false
                else -> openExternalUri(view.context, uri)
            }
        }
    }

    webView.loadUrl(checkoutUrl)
}

private fun openExternalUri(context: Context, uri: Uri): Boolean {
    return try {
        context.startActivity(Intent(Intent.ACTION_VIEW, uri))
        true
    } catch (_: ActivityNotFoundException) {
        false
    }
}

还需要在 AndroidManifest.xml 中声明网络权限:

<uses-permission android:name="android.permission.INTERNET" />

checkoutUrl 应根据店铺的实际路由生成,不要在 App 中永久写死某个版本的结账路径。

方案二:原生购物车交给 WebView 结账

如果商品和购物车由 App 管理,建议增加一个服务端“结账交接”接口。

具体流程如下:

  1. App 将商品、数量和当前用户身份发送给业务服务端。
  2. 服务端验证用户身份、商品价格、库存和数量。
  3. 服务端通过 PrestaShop 的服务层、模块代码或 Webservice 创建购物车。
  4. 服务端生成一个短期有效且只能使用一次的随机 token。
  5. App 获取形如 https://shop.example.com/mobile-checkout?t=... 的地址。
  6. WebView 打开该地址。
  7. PrestaShop 端验证 token,并建立或恢复合法会话。
  8. 服务端返回 Set-Cookie,然后重定向到实际结账页面。
  9. 支付完成或取消后,网页跳转到约定的 HTTPS 地址或 App Deep Link。

交接 token 中不应直接包含可修改的价格、客户 ID 或购物车内容。服务端可以保存类似下面的数据:

{
  "token_hash": "sha256(...)",
  "customer_id": 123,
  "cart_id": 456,
  "expires_at": "2026-09-01T10:05:00Z",
  "used": false
}

交接入口的处理逻辑可以概括为:

<?php

$token = $_GET['t'] ?? '';
$handoff = findCheckoutHandoffByToken($token);

if (
    $handoff === null ||
    $handoff->used ||
    $handoff->expiresAt < new DateTimeImmutable()
) {
    http_response_code(403);
    exit('Invalid or expired checkout link');
}

markHandoffAsUsed($handoff);

// 以下操作必须使用当前 PrestaShop 版本支持的 Context、Cart、Customer
// 或模块服务完成,不应自行拼接 PrestaShop Cookie。
restorePrestashopCheckoutContext(
    $handoff->customerId,
    $handoff->cartId
);

redirectToPrestashopCheckout();

findCheckoutHandoffByToken()、restorePrestashopCheckoutContext() 和 redirectToPrestashopCheckout() 都是示意函数。实际使用哪种 PrestaShop 调用方式,取决于当前版本和模块架构。这部分应实现为店铺模块或受控的服务端代码。

如果使用 PrestaShop Webservice,还要注意:Webservice 可以管理购物车等资源,但通常无法直接把 API 购物车转换成浏览器中的已登录会话。会话关联仍需通过服务端交接入口完成。

支付完成后的回调处理

可以让支付成功页跳转到你控制的 HTTPS 地址,例如:

https://shop.example.com/mobile-payment/complete?order_reference=...

Android 识别到该路径后,可以结束支付页面:

override fun shouldOverrideUrlLoading(
    view: WebView,
    request: WebResourceRequest
): Boolean {
    val uri = request.url

    if (
        uri.scheme == "https" &&
        uri.host == "shop.example.com" &&
        uri.path == "/mobile-payment/complete"
    ) {
        onCheckoutCompleted()
        return true
    }

    return when (uri.scheme?.lowercase()) {
        "http", "https" -> false
        else -> openExternalUri(view.context, uri)
    }
}

App 收到成功跳转后,不能只凭 URL 判断订单已经支付。还应向业务服务端查询订单状态,由服务端核对 PrestaShop 中的订单和支付状态。

支付模块兼容性

结账流程能否完整嵌入 WebView,取决于所使用的支付模块。下面这些场景通常需要单独处理:

  • 3-D Secure 验证;
  • 支付 App 跳转;
  • intent://、自定义 scheme 或 Universal Link;
  • 新窗口和弹窗;
  • 跨站 Cookie;
  • 支付服务商明确禁止使用嵌入式浏览器;
  • 银行页面要求完整的浏览器环境。

如果支付服务商不支持 WebView,应改用 Chrome Custom Tabs 或系统浏览器完成认证,再通过 HTTPS App Link 返回 App。不要尝试忽略 SSL 错误、修改 User-Agent 或注入 JavaScript 来绕过限制。

安全注意事项

  • 所有结账页面和接口都必须使用 HTTPS。
  • 不要把 PrestaShop 加密密钥、Webservice 密钥或管理员凭据放进 App。
  • 交接 token 应随机生成、短期有效且只能使用一次,服务端只保存其哈希值。
  • 商品价格、折扣、运费和库存必须由服务端重新计算。
  • Cookie 应正确设置 Secure、HttpOnly 和合适的 SameSite 属性。
  • WebView 只应信任店铺域名和明确允许的支付域名。
  • 不要启用不必要的文件访问,也不要向不可信页面暴露 addJavascriptInterface。
  • 不要在 onReceivedSslError() 中无条件调用 handler.proceed()。
  • 支付是否成功,必须以服务端确认的订单状态为准,不能依赖客户端页面的提示。

需要解决的问题不是“怎样伪造 PrestaShop Cookie”,而是“怎样由服务端把原生购物车安全地交接给网页会话”。只要会话由 PrestaShop 服务端创建,Android WebView 就会像普通浏览器一样保存 Cookie,并继续完成结账。

备注:内容仅供参考。