> ## Documentation Index
> Fetch the complete documentation index at: https://waffo.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# 接入方式怎么选，以及把收银台嵌进我的页面

> 解答收银台接入方式最常见的问题：托管收银台与自建支付页怎么选，跳转、新窗口、iframe 与 App WebView 的用法和限制，前端 SDK 的能力，以及 iframe 高度自适应和跨站 cookie。

本页回答两类问题：一是该用 Waffo 托管收银台还是自己做支付页；二是选了托管收银台之后，怎么把它放进自己的页面（跳转、新窗口、iframe、App WebView），以及 iframe 里的高度自适应和跨站 cookie 限制。各步骤的完整代码以官网[收银台集成步骤](https://waffo.com/docs/zh/developer-docs/integration/checkout/steps.md)为准。

## 我该选 Waffo 托管收银台，还是自己做支付页？

Waffo 支持两种收银台集成模式：

| 维度 | Waffo 托管收银台 | 商户自建收银台（直接调 API） |
| - | - | - |
| 支付方式选择 | 用户在 Waffo 收银台选 | 用户在你的页面选 |
| 集成复杂度 | 低（跳转即可） | 高（自建界面，并在请求里指定支付方式） |
| 支付方式覆盖 | 自动覆盖全部可用方式，新增方式无需改代码 | 需要自己维护支持列表 |
| PCI DSS | 由 Waffo 处理 | 涉及卡支付时由商户负责 |
| 界面定制 | 支持主题色、Logo、语言等 | 完全自定义 |
| 适合谁 | 大多数商户 | 有自有收银台的大商户 |

两种模式在下单请求上的区别：

* **托管收银台**：不需要传 `payMethodType` 或 `payMethodName`，收银台自动展示全部可用支付方式。
* **自建收银台**：用户已在你的页面选好，需要告诉 Waffo 走哪个通道。卡支付只传 `payMethodType`（Waffo 通过卡 BIN 自动识别发卡品牌，不要传 `payMethodName`）；虚拟账户（VA）只传 `payMethodType: "VA"`，具体银行由用户在 Waffo 侧选；电子钱包传 `payMethodName` 指定钱包，用户会跳过收银台直接进入该钱包的支付流程。

自建收银台的细节见[自建收银台页面而不使用 Waffo 的收银台](https://waffo.com/docs/zh/support/merchant-built-checkout-faq.md)，各支付方式的参数规则见官网[收银台定制](https://waffo.com/docs/zh/developer-docs/integration/checkout/customization.md)页面。

## 收到 `orderAction` 之后，有哪几种方式让用户打开收银台？

| 方式 | 适合场景 | 要点 |
| - | - | - |
| 整页跳转（redirect） | 网站、手机网页、App 均可，最省事 | 打开 `orderAction.webUrl`；支付结果以 Webhook 或主动查询为准，不要只看跳转回来的页面 |
| `window.open` 新窗口 | 不想离开当前页 | 见下一节 |
| iframe 内嵌 | 要把用户留在当前网页 | 见下面几节；必须满足一组配置要求 |
| App WebView | 要把用户留在你的 App 内 | 见[在我自己的 App 里加载收银台（WebView、App 唤起、返回）](https://waffo.com/docs/zh/support/checkout-in-app-webview-faq.md) |

没有特别需要留在页面内的理由时，优先选整页跳转：它对页面容器和前端的依赖最少。

## 用 `window.open` 打开收银台有什么坑？

* 在点击事件处理函数里**同步**调用 `window.open()`，没有问题。
* 在 `await` 之后再调用，**会被浏览器拦截**。做法：先同步打开一个空白窗口，等拿到地址后再给它赋 URL；或者弹一个确认框，让用户自己点击打开。
* 苹果设备的移动端对用户手势判定很严，异步打开几乎都会被拦截。

## 把收银台放进 iframe，必须满足什么条件？

| 要求 | 说明 |
| - | - |
| iframe 标签带 `allow="payment"` | 否则 Google Pay 等支付方式在 Android 设备上无法使用 |
| Referrer Policy 设为 `strict-origin-when-cross-origin` | 否则微信支付会失败 |
| 不要写死宽度（例如 `width: 700px`） | 不同设备上内容会被裁切，用响应式布局；移动端也要检查 iframe 没有固定尺寸，否则会渲染成横版 PC 页面 |
| CSP 的 `frame-src` 只放行各环境对应的 Waffo 收银台域名 | 3DS 验证页由 Waffo 收银台自己处理，你的页面不需要把发卡行的验证域名加进 `frame-src`。不要为此放开所有 `https:` 来源，也不要关掉 CSP |

如果你的页面无法安全地支持 iframe 里的 3DS 验证，请改用整页跳转或 `window.open`。

遇到 3DS 页面空白时，先到浏览器控制台和网络面板记录被拦截的 CSP 指令、被拦截的网址、下发该策略的文档、集成方式、订单号和时间，再联系技术支持。可以先用 `Content-Security-Policy-Report-Only` 验证策略，再正式启用。

### 哪些支付方式不能放进 iframe 或要特别处理？

| 支付方式 | 限制 |
| - | - |
| Apple Pay | iframe 里必须使用 Waffo 前端 SDK，并**提前**联系技术支持完成域名验证和注册；不要把 `orderAction.webUrl` 放进普通 iframe。详见[Apple Pay 与 Google Pay 的接入方式](https://waffo.com/docs/zh/support/apple-pay-and-google-pay-integration-faq.md) |
| JKOPAY | 不支持 iframe 加载，请改用跳转或 `window.open` |
| Google Pay | 虚拟商品不能在 App WebView 里使用；iOS WebView 可能需要额外配置。可改在外部浏览器打开 |

更多按支付方式区分的限制，见官网[支付方式集成注意事项](https://waffo.com/docs/zh/developer-docs/tools-and-references/references/payment-method-integration-notes.md)。可用的支付方式始终取决于你的合同、Portal 的 Payin 配置和支付方式配置查询结果。

## Waffo 前端 SDK 能帮我做什么？

Waffo 的前端 SDK 是 npm 包 `@waffo/payment-sdk`，在浏览器里运行，和服务端 SDK 是两回事。初始化时传入你的 client API key，并指定环境（`prod`、`testing` 或 `sandbox`）。它提供两项能力：

| 能力 | 方法 | 用途 |
| - | - | - |
| 卡 Tokenization | `sdk.tokenizationSubmit()` | 在你的页面收集卡信息，加密后提交，换取 token。你的后端不保留、不传输明文卡数据时，无需自己通过 PCI DSS 认证 |
| 内嵌收银台 | `WaffoSDK.renderIframe()` | 把 Waffo 收银台以 iframe 形式渲染进你的页面，支持主题色、Logo、Apple Pay / Google Pay 按钮和多语言 |

使用 `tokenizationSubmit` 之前，你的后端要先调用 Tokenization 的 Generate 接口，拿到 `tokenSessionId`。完整流程见[Tokenization 与卡支付 FAQ](https://waffo.com/docs/zh/support/tokenization-faq.md)和官网[前端组件 - 组件使用](https://waffo.com/docs/zh/developer-docs/integration/elements/usage.md)。

收银台的主题、语言、支付方式筛选，是在下单请求的 `paymentInfo` 里配置的，见[客制化收银台页面（品牌、颜色、Logo）](https://waffo.com/docs/zh/support/cashier-customization-faq.md)和[收银台语言](https://waffo.com/docs/zh/support/cashier-language-faq.md)。

## 我把收银台放在 iframe 里，高度怎么自适应？

默认情况下，iframe 里的收银台不会通知外层页面它有多高，于是容易出现双滚动条或底部大片空白。做法是在**收银台地址上带 `embed=adaptive`**，收银台就会把自己内容的自然高度报告给外层页面。

**报告的消息格式**（iframe 向它的直接父窗口发送）：

```javascript theme={null}
{ type: 'checkout.resize', height: 684 } // 整数，单位 CSS 像素
```

要点：

* 只有 `embed=adaptive` 的 iframe 才会发这个消息。没带的 iframe、以及独立打开的收银台页面，保持原来的最小高度，也不发任何消息。原有的 `embed=true` 行为不变。
* 消息里**只有高度**，不包含订单或支付数据。
* 高度变化会被合并后再发送，不会每一次布局抖动都发一条。
* 外层页面**必须自己监听**这个消息并设置 iframe 高度，收银台只负责发送这一侧。

**外层页面的监听示例**（在加载 iframe 之前注册）：

```javascript theme={null}
const frame = document.querySelector('#checkout iframe')
const checkoutOrigin = new URL(frame.src, location.href).origin

function onCheckoutMessage(event) {
  if (event.origin !== checkoutOrigin || event.source !== frame.contentWindow) return
  const data = event.data
  if (data?.type !== 'checkout.resize') return
  if (!Number.isFinite(data.height) || data.height <= 0) return
  frame.style.height = `${Math.ceil(data.height)}px`
}

window.addEventListener('message', onCheckoutMessage)
```

注意事项：

* **一定要同时校验 `event.origin` 和 `event.source`**。收银台发消息时目标来源写的是 `*`（为了支持任意商户域名和没有 referrer 的嵌入），所以安全校验要由你这边做。
* 外层容器的高度保持自动，去掉会阻止收缩的固定高度或最小高度；加载期间可以先给一个初始高度。同步生效之前，不要用 `height: 100% !important` 覆盖结果，也不要先把滚动关掉。
* 如果监听器注册得晚，或者容器被隐藏后重新显示，可以主动要求重新测量（即使高度没变也会重发一次）：

```javascript theme={null}
frame.contentWindow.postMessage({ type: 'checkout.requestResize' }, checkoutOrigin)
```

* 只有来自**直接父窗口**的请求会被响应。
* 收银台销毁时，记得移除你页面上的监听器。

## 收银台嵌在我的站点 iframe 里，国家或时区识别不到，怎么办？

收银台会用浏览器里的 `country`（订单所属国家）和 `tz`（时区）两个 cookie。当收银台处在**与你的站点不同源（跨站）的 iframe** 里时，普通的 cookie 既写不进去也不会被带上；收银台因此在 HTTPS 下把它们写成 `SameSite=None; Secure; Partitioned` 的分区 cookie，让它们在嵌入场景下仍然可以工作。

对你的含义：

| 情况 | 说明 |
| - | - |
| 你的页面和收银台都走 HTTPS | 收银台会按上面的方式写入，通常无需你做任何事 |
| 本地用 HTTP 调试 | 无法设置 `Secure`，会退回普通 cookie，在跨站 iframe 里国家和时区可能识别不到。这是调试环境的限制，请用 HTTPS 验证 |
| 在 Safari 或 Chrome 里国家、时区识别不到，或支付方式列表和预期不一致 | 先确认是 HTTPS，并检查浏览器是否彻底禁用了站点数据存储；仍然复现时，把浏览器版本、嵌入页面地址、订单号提供给技术支持 |

这套 cookie 行为是收银台自动处理的，你不需要在自己的页面里写任何 cookie。

## 相关入口

* 官网收银台集成步骤：[https://waffo.com/docs/zh/developer-docs/integration/checkout/steps.md](https://waffo.com/docs/zh/developer-docs/integration/checkout/steps.md)
* 官网集成方式对比：[https://waffo.com/docs/zh/developer-docs/use-cases/checkout-solution.md](https://waffo.com/docs/zh/developer-docs/use-cases/checkout-solution.md)
* 官网前端组件使用：[https://waffo.com/docs/zh/developer-docs/integration/elements/usage.md](https://waffo.com/docs/zh/developer-docs/integration/elements/usage.md)
* 官网支付方式集成注意事项：[https://waffo.com/docs/zh/developer-docs/tools-and-references/references/payment-method-integration-notes.md](https://waffo.com/docs/zh/developer-docs/tools-and-references/references/payment-method-integration-notes.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.